Android Question Strange issues with socket/asyncstream [Solved]

Discussion in 'Android Questions' started by Kevin, Jun 20, 2018.

  1. Kevin

    Kevin Well-Known Member Licensed User

    I used the B4X Serializator tutorial as the basis for an android app that communicates with my ESP-12E. For the most part it works great, however, I have noticed some strange issues:

    If I don't "disconnect" when pausing the activity (such as when exiting the app), then it seems to stay connected, which is fine. However, if I then want to connect to it using the B4J app, it cannot connect, presumably because the ESP is still connected to the Android app.

    So in Activity_Pause of the Android app, I call the Disconnect sub in the Starter service. This solves the problem above, and works fine if I am using the ESP's IP address in the B4A app. In this case, opening the app again later re-connects or if I use the B4J app then it works and all is well.

    But if I use my DDNS URL (with a port forwarded in the router to the ESP, with the router likely using hairpin routing), it always fails to re-connect when using the B4A app again. However, if I do NOT call Disconnect when pausing the B4A app, it continues to work when opening the app again later.

    Another problem I am having is that when away from my WiFi (or if I just switch my phone over to 4G), when using the DDNS URL for the ESP, it cannot connect. The port is definitely forwarded to the correct port and IP address of the ESP. I have many devices on my network with forwarded ports and they all connect fine using the DDNS URL.

    I'm at a loss for why I am seeing this strange behavior. I hope I have explained it well.

    In short:

    1 On my local WiFI, if I disconnect/close the connection when the B4A app pauses and later open the B4A app again:
    *Using the ESP's IP address re-connects fine
    *Using my DDNS URL instead of the IP address causes the app to fail to re-connect.
    *I can switch back and forth between the B4A and B4J apps and they connect fine.

    2 On my local WiFI, if I do NOT disconnect/close the connection when the B4A app pauses and later open the B4A app again:
    *Using the ESP's IP address re-connects fine
    *Using my DDNS URL instead of the IP address re-connects fine.
    *I cannot then use the B4J app to connect to the ESP because it is still connected to the B4A app.

    3 Even though I have the proper port forwarded in my router pointing to the ESP, I cannot connect to it outside of my WiFi network using DDNS, even though this works fine with other network devices with their ports forwarded in my router.

    MAIN:
    Code:
    #Region  Project Attributes
        
    #ApplicationLabel: Pool Minder
        
    #VersionCode: 1
        
    #VersionName: 1.0
        
    'SupportedOrientations possible values: unspecified, landscape or portrait.
        #SupportedOrientations: portrait
        
    #CanInstallToExternalStorage: False
        
    #BridgeLogger: True
    #End Region

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

    ' Data Objects:
    ' 0 = AirTemperature
    ' 1 = AirHumidity
    ' 2 = AirHeatIndex
    ' 3 = PoolTemperature
    ' 4 = HeatPumpRun (boolean)

    Sub Process_Globals
        
    'These global variables will be declared once when the application starts.
        'These variables can be accessed from all modules.

    End Sub

    Sub Globals

        
    Private PanelConfig As Panel
        
    Private btnConnectAPMode As Button
        
        
    Private txtIP As EditText
        
    Private txtBoardName As EditText
        
    Private txtSSID As EditText
        
    Private txtPW As EditText
        
        
    Private PanelMain As Panel
        
    Private btnRefresh As Button
        
    Private btnConfig As Button
        
    Private lblPoolTemp As Label
        
    Private lblAirTemp As Label
        
    Private lblHumidity As Label
        
    Private lblHeatIndex As Label
        
    Private lblHeatRun As Label
        
        
    Private btnHeatRelayToggle As Button
        
    Private lblDandT As Label
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
        
    Activity.LoadLayout("1")
        
        Starter.BoardIP = StateManager.GetSetting2 (
    "sIP""192.168.4.1")
        
    Log ("Board IP = " & Starter.BoardIP)
        txtIP.Text = Starter.BoardIP
    End Sub

    Public Sub StateChanged
    '    If Starter.connected Then
    '        lblState.Text = "Connected"
    '    Else
    '        lblState.Text = "Disconnected"
    '    End If
    End Sub

    Sub Activity_Resume
        btnRefresh_Click 
    'ToDo  Disabled for now until further testing
    End Sub

    Sub Activity_Pause (UserClosed As Boolean)
        
    If Starter.BoardIP <> Starter.BoardIPAPMode Then
            StateManager.SetSetting (
    "sIP", Starter.BoardIP)
            StateManager.SaveSettings
        
    End If
        
    'If UserClosed Then CallSub (Starter, "Disconnect")
        'CallSub (Starter, "Disconnect") '<-------------------------------------------------
    End Sub

    Sub Activity_KeyPress (KeyCode As Int) As Boolean 'Return True to consume the event
        If KeyCode = KeyCodes.KEYCODE_BACK And PanelConfig.Visible = True Then
            SaveAppSettings
            PanelConfig.Visible = 
    False
            
    Return True
        
    End If
    End Sub

    Sub SaveAppSettings
        
    If Starter.BoardIP <> Starter.BoardIPAPMode Then
            StateManager.SetSetting (
    "sIP", Starter.BoardIP)
            StateManager.SaveSettings
        
    End If
    End Sub

    #Region Main Screen

    Sub btnRefresh_Click
        Starter.Mode = 
    3
        
    ProgressDialogShow ("Refreshing data...")
        
    CallSub2 (Starter, "Connect"True)
    End Sub

    Sub btnSetConfig_Click
        
    If txtIP.Text = "" Then
            
    Msgbox ("Please enter the Pool Minder device's IP address."Application.LabelName)
             
    Return
        
    End If
        
        
    If txtSSID.text = "" Or txtPW.Text = "" Then
            
    Msgbox ("Please enter your WiFi SSID and PW."Application.LabelName)
            
    Return
        
    End If
        
        
    Dim p As Phone
        p.HideKeyboard (
    Activity)
        
        Starter.Mode = 
    2
        
    ProgressDialogShow ("Uploading configuration...")
        
    CallSub2 (Starter, "Connect"False)
    End Sub

    Sub btnGetConfig_Click
        
    If txtIP.Text = "" Then
            
    Msgbox ("Please enter the Pool Minder device's IP address."Application.LabelName)
            
    Return
        
    End If
        
        txtBoardName.Text = 
    ""
        txtSSID.Text = 
    ""
        txtPW.Text = 
    ""
        
        
    Dim p As Phone
        p.HideKeyboard (
    Activity)
        
        Starter.Mode = 
    1
        
    ProgressDialogShow ("Retrieving configuration")
        
    CallSub2 (Starter, "Connect"True)
    End Sub

    Sub btnConfig_Click
        PanelConfig.Visible = 
    True
    End Sub

    Sub UpdateDataFields (Record As BoardData)
        
    ' Data Objects:
        ' 0 = AirTemperature
        ' 1 = AirHumidity
        ' 2 = AirHeatIndex
        ' 3 = PoolTemperature
        ' 4 = HeatPumpRun (boolean)
        ' 5 = Date & Time
        lblDandT.Text = Record.DandT
        lblAirTemp.Text = Record.AirTemp & 
    " F"
        lblHumidity.Text = Record.AirHumidity & 
    "%"
        lblHeatIndex.Text = Record.AirHeatIndex & 
    " F"
        lblPoolTemp.Text = Record.PoolTemp & 
    " F"
        
    Select Case Record.HeatPumpRun
            
    Case True
                lblHeatRun.Text = 
    "Auto"
            
    Case False
                lblHeatRun.Text = 
    "Standby"
        
    End Select

    End Sub

    Sub btnHeatRelayToggle_Click
        
    If lblHeatRun.Text = "" Then
            
    Msgbox ("Please sync the app with the Pool Minder device first in order to retrieve the current setting."Application.LabelName)
        
    End If
        
        
    Select Case lblHeatRun.Text
                
    Case "Auto"
                    Starter.Mode = 
    5 ' Change to Standby (HEATOFF)
                    ProgressDialogShow ("Please wait...")
                    
    CallSub2 (Starter, "Connect"False)
                
    Case "Standby"
                Starter.Mode = 
    4 ' Change to Auto (HEATON)
                ProgressDialogShow ("Please wait...")
                    
    CallSub2 (Starter, "Connect"False)
        
    End Select
    End Sub

    Sub HideProg
        
    ProgressDialogHide
    End Sub

    #End Region

    #Region MessageBoxes and Toasts
    Sub ShowMessage (sMessage As String)
        
    Msgbox (sMessage, Application.LabelName)
    End Sub

    Sub ShowToast (sMessage As String)
        
    ToastMessageShow (sMessage, True)
    End Sub
    #End Region


    #Region Config Screen

    Sub btnConnectAPMode_Click
        
    If txtIP.Text <> Starter.BoardIPAPMode Then
            txtIP.Text = Starter.BoardIPAPMode
                
    Else ' It is equal to it
                    txtIP.Text = StateManager.GetSetting ("sIP")
        
    End If
    End Sub

    Sub txtIP_TextChanged (Old As String, New As String)
        Starter.BoardIP = New
    End Sub

    Sub txtBoardName_TextChanged (Old As String, New As String)
        Starter.rec.BoardName = New
    End Sub

    Sub txtSSID_TextChanged (Old As String, New As String)
        Starter.rec.SSID = New
    End Sub

    Sub txtPW_TextChanged (Old As String, New As String)
        Starter.rec.Password = New
    End Sub

    Sub UpdateEditTexts (Record As BoardConfig)
        txtBoardName.Text = Record.BoardName
        txtSSID.Text = Record.SSID
        txtPW.Text = Record.Password
    End Sub

    #End Region


    #Region Panel Click Events
    Sub PanelConfig_Click
    End Sub

    Sub PanelMain_Click
    End Sub
    #End Region
    STARTER SERVICE:
    Code:
    #Region  Service Attributes
        
    #StartAtBoot: False
        
    #ExcludeFromLibrary: True
    #End Region

    ' Data Objects:
    ' 0 = AirTemperature
    ' 1 = AirHumidity
    ' 2 = AirHeatIndex
    ' 3 = PoolTemperature
    ' 4 = HeatPumpRun (boolean)

    Sub Process_Globals
        
    Private sock As Socket
        
    Private astream As AsyncStreams
        
    Public connected As Boolean
        
    'Private watchdog As Timer
        'Private lastValueTime As Long
        Private ser As B4RSerializator
        
        
    Public BoardIP As String 'IP address of ESP board
        Public BoardIPAPMode As String = "192.168.4.1" 'Default IP for ESP-12E
        
        
    Public Mode As Int '1 = GETCONFIG, 2 = SETCONFIG, 3 = GETDATA, 4 = SETHEATON, 5 - SETHEATOFF
        Type BoardData (AirTemp As String, AirHumidity As String, AirHeatIndex As String, PoolTemp As String, HeatPumpRun As Boolean, DandT As String)
        
    Type BoardConfig (BoardName As String, SSID As String, Password As String)
        
    Public rec As BoardConfig
        
    Private dat As BoardData
    End Sub

    Sub Service_Create
        
    'watchdog.Initialize("watchdog", 1000)
        ser.Initialize
        rec.Initialize
    End Sub

    Sub Service_Start (StartingIntent As Intent)

    End Sub

    Public Sub Connect (Get As Boolean)
        
    If sock.IsInitialized = False Then sock.Initialize("sock")
        
        
    ' New
    '    If astream.IsInitialized Then
    '        astream.Close
    '    End If
    '    sock.Initialize("sock")
        '------------------
        
        
    Try
            
    If sock.Connected = False Then
                sock.Connect(BoardIP, 
    5104110000)
                
    Wait For sock_Connected (Successful As Boolean)
                
    If Successful = False Then
                    
    CallSub(Main, "HideProg")  'Hide the progress object
                    CallSub2(Main, "ShowMessage""Failed to connect")
                    
    Log("Failed to connect!")
                    
    Return
                
    Else
                    
    Log("Connected to board")
                
    End If
                astream.InitializePrefix(sock.InputStream, 
    False, sock.OutputStream, "astream")
            
    End If
        
    Catch
            
    Log(LastException)
            
    If astream.IsInitialized Then astream.Close
            sock.close
            
    CallSub(Main, "HideProg")  'Hide the progress object
            Return
        
    End Try
        
        
    If Get Then
                    
    Select Case Mode
                        
    Case 1 'GETCONFIG
                            Log ("Requesting Config...")
                            
    Dim HeaderObj As Object = Array ("GETCONFIG")
                            astream.Write(ser.ConvertArrayToBytes(HeaderObj))
                            
    Wait For astream_NewData (Buffer() As Byte)
                            
    If Buffer(0) = 0 Then
                                rec.BoardName = 
    "PoolMinder"
                            
    Else
                                
    ' We received a 1 as the first byte sent, which means it has been configured
                                Log ("Board appears to have been configured")
                                
    Wait For (astream) AStream_NewData(Data() As Byte)
                                
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                                
    Log (ObjectsReceived(0))
                                
    Select Case ObjectsReceived(0)
                                    
    Case "GETCONFIG"
                                        
    Log ("Config Header received")
                                        
    Wait for astream_NewData (Data() As Byte)
                                        
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                                        
    For Each o As Object In ObjectsReceived
                                            
    Log("Incoming Object: " & o)
                                        
    Next
                                        rec = ObjectsToRecord(ser.ConvertBytesToArray(Data))
                                    
    Case Else
                                        
    Log ("Data was received but without a header!")
                                        
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                                        
    For Each o As Object In ObjectsReceived
                                            
    Log("Incoming Object: " & o)
                                        
    Next
                                
    End Select
                            
    End If
                            
    CallSub2 (Main, "UpdateEditTexts", rec)   'UpdateEditTexts (rec) 'sheet.Set(rec, meta)
                        Case 3 'GETDATA
                            Log ("Requesting Data...")
                            
    Dim HeaderObj As Object = Array ("GETDATA")
                            astream.Write(ser.ConvertArrayToBytes(HeaderObj))
                            
    'Sleep(500)
                            Wait for astream_NewData (Data() As Byte)
                            
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                            
    If ObjectsReceived(0) = "GETDATA" Then
                                
    Wait for astream_NewData (Data() As Byte)
                                
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                                
    For Each o As Object In ObjectsReceived
                                    
    Log("Incoming Object: " & o)
                                
    Next
                                dat = ObjectsToRecordDat(ser.ConvertBytesToArray(Data))
                            
    End If
                            
    CallSub2 (Main, "UpdateDataFields", dat)  ' Update data labels
                    End Select
                
    Else 'Put mode, so send Request header and anything else relevant
                    Select Case Mode
                        
    Case 2 'SETCONFIG
                            Log ("Sending Config...")
                            
    Dim HeaderObj As Object = Array ("SETCONFIG")
                            astream.Write(ser.ConvertArrayToBytes(HeaderObj))
                            
    'Sleep(250)
                            astream.Write(ser.ConvertArrayToBytes(RecordToObjects(rec)))
                            
    'Sleep(250)
                            Wait for astream_NewData (Data() As Byte)
                            
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                            
    If ObjectsReceived(0) = "SETCONFIG-SUCCESS" Then
                                
    CallSub2 (Main, "ShowToast""Config settings saved!")
                                
    Log ("CONFIG command was successful")
                            
    End If
                        
    Case 4 'SETHEATON
                            Log ("Sending HEATON request...")
                            
    Dim HeaderObj As Object = Array ("SETHEATON")
                            astream.Write(ser.ConvertArrayToBytes(HeaderObj))
                            
    'Sleep(250)
                            Wait for astream_NewData (Data() As Byte)
                            
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                            
    If ObjectsReceived(0) = "SETHEAT-SUCCESS" Then
                                
    Log ("SETHEAT command was successful")
                            
    End If
                            
    'Disconnect
                            CallSub (Main, "btnRefresh_Click")
                        
    Case 5 'SETHEATOFF
                        Log ("Sending HEATOFF request...")
                            
    Dim HeaderObj As Object = Array ("SETHEATOFF")
                            astream.Write(ser.ConvertArrayToBytes(HeaderObj))
                            
    'Sleep(250)
                            Wait for astream_NewData (Data() As Byte)
                            
    Dim ObjectsReceived() As Object = (ser.ConvertBytesToArray(Data))
                            
    If ObjectsReceived(0) = "SETHEAT-SUCCESS" Then
                                
    Log ("SETHEAT command was successful")
                            
    End If
                            
    'Disconnect
                            CallSub (Main, "btnRefresh_Click")
                    
    End Select   
                
    End If
            
    'Disconnect
        CallSub (Main, "HideProg")
    End Sub

    Sub RecordToObjects (Record As BoardConfig) As Object()
    '    Return Array(Record.BoardName, Record.SSID, Record.Password, Record.AirTemp, Record.AirHumidity, Record.PoolTemp, Record.hpStatus, _
    '        Record.hpDay1, Record.hpDay2, Record.hpDay3, Record.hpDay4, Record.hpDay5)
        Return Array(Record.BoardName, Record.SSID, Record.Password)
    End Sub

    Sub ObjectsToRecord(Objects() As Object) As BoardConfig
        
    Dim r As BoardConfig
        r.Initialize
        r.BoardName = Objects(
    0)
        r.SSID = Objects(
    1)
        r.Password = Objects(
    2)
    '    r.hpDay1 = Objects(7)
    '    r.hpDay2 = Objects(8)
    '    r.hpDay3 = Objects(9)
    '    r.hpDay4 = Objects(10)
    '    r.hpDay5 = Objects(11)
    '    r.MQTTBroker = Objects(3)
    '    r.MQTTPort = Objects(4)
    '    r.MQTTUsername = Objects(5)
    '    r.MQTTPassword = Objects(6)
        Return r
    End Sub

    Sub ObjectsToRecordDat(Objects() As Object) As BoardData
        
    Dim r As BoardData
        r.Initialize
        
    ' Data Objects:
        ' 0 = AirTemperature
        ' 1 = AirHumidity
        ' 2 = AirHeatIndex
        ' 3 = PoolTemperature
        ' 4 = HeatPumpRun (boolean)
        ' 5 = Date & Time
        r.AirTemp = Objects(0)
        r.AirHumidity = Objects(
    1)
        r.AirHeatIndex = Objects(
    2)
        r.PoolTemp = Objects(
    3)
        r.HeatPumpRun = Objects (
    4)
        r.DandT = Objects (
    5)
        
    Return r
        
    '    r.hpDay1 = Objects(7)
        '    r.hpDay2 = Objects(8)
        '    r.hpDay3 = Objects(9)
        '    r.hpDay4 = Objects(10)
        '    r.hpDay5 = Objects(11)
    End Sub

    Sub AStream_Error
        
    Log("Error")
        
    CallSub(Main, "HideProg")  'Hide the progress object
        If astream.IsInitialized Then astream.Close
        sock.close
    End Sub

    Sub AStream_Terminated
        
    Log("Terminated")
        
    CallSub(Main, "HideProg")  'Hide the progress object
        If astream.IsInitialized Then astream.Close
    End Sub

    Sub Disconnect
        
    Log("Disconnect")
        
    CallSub(Main, "HideProg")  'Hide the progress object
        If astream.IsInitialized Then astream.Close
        sock.Close
    End Sub

    'Return true to allow the default exceptions handler to handle the uncaught exception.
    Sub Application_Error (Error As Exception, StackTrace As StringAs Boolean
        
    CallSub(Main, "HideProg")  'Hide the progress object
        Return True
    End Sub

    Sub Service_Destroy
        
    CallSub(Main, "HideProg")  'Hide the progress object
    End Sub
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    It is possible that this is related to the other issue as well. Worth testing it with airplane mode turned on and wifi turned on.
     
  3. Kevin

    Kevin Well-Known Member Licensed User

    I will do more investigating when I have the time.

    Based on what I am describing, do you feel that it would point to a problem in my code? If so, what should I be looking for, and would it be in the B4R server code or the B4A client code? Would you recommend disconnecting after every exchange of messages is complete? Or only when the app is finished (closed)?
     
  4. Kevin

    Kevin Well-Known Member Licensed User

    Just a quick update as I have solved the problem.

    I had been including the http:// when entering the DDNS URL in place of the IP address. For whatever reason that occasionally worked, which threw me off. I just now tried it without the http:// and I was able to connect & reconnect just fine even from outside of my home WiFi.

    If it would help anyone else, regarding my question about when I should disconnect, I am keeping the connection open unless the B4A app pauses or if the user changes the IP address field in the app.
     
  5. KMatle

    KMatle Expert Licensed User

    I disconnect in Activity_Pause and reconnect in _Resume (works in all of my apps no matter which way I establish a connection like BT or Network)
     
    Kevin likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice