Android Question Problem with TCP socket Android<-->ESP

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

in my 3D printer host app I've a problem than cannot know how to solve.
I'm explain better...

My app communicate over TCP socket with ESP8266 / ESP32 to control a 3D printer, it send and
receive commands. ESP act as server and as WiFi<->Serial bridge, my app as client and connect to ESP.

Because the ESP sketch in written in C++ I avoided to use a stream in Prefix mode and used the standard way,
this forced me to collect on both side the data, not simple in C++ but after a lots of changes worked well.
I do not used AstreamText because I reuse the same socket to send string commands and even to upload
gcode files (as binary data) from Android to ESP SD Card, I just replicated what AstreamText do using a StringBuilder to collect data and CRLF as delimiter, only on the send command sub (strings).

Every command I send to ESP it send to a 3D printer, ESP read response from 3D printer and return back (the original command and reply from 3D printer) to Android as log.
My problem is that when I connect to ESP, ESP send a command to a printer to request the temperature, then send it back to Android that read it and draw a temperature graph.

If in my app I do some things, eg, I call a Sub that send over socket, it send and then the sub Astream_NewData is fired without that my sub finish to execute, this is a reply from ESP that arrives.

I want to know if there is a way to deactivate for some time the data reception, eg when I upload a file to ESP. I tried to add a boolean flag but without success.

Better explained with code:

B4X:
' Dim Success As Boolean = SendCommand("MyCommand")
Wait For (SendCommand("MyCommand")) Complete (Success As Boolean)
If Success Then ...
'....
'....
Sub SendCommand(cmd As String) As Boolean
  Wait For (SendSocket(cmd)) Complete (Success as Boolean)  ' >>> SEND DATA
 
  If Success Then   ' This piece of code is never called because SocketAStreams_NewData is called before, then the code do not continue here
     Log("SUCCESS")
  Else
     Log("Cannot send data, queed is full")
  End Sub
  Return Success
End Sub

Sub SendSocket (cmd As String) As ResumableSub
  Return SocketAStreams.Write(cmd.GetBytes("UTF8"))
End Sub

Sub SocketAStreams_NewData (Buffer() As Byte)  ' <<< RECEIVE DATA
  [read and collect data]
End Sub

Many thanks
 
Last edited:

max123

Well-Known Member
Licensed User
Longtime User
Sorry, a typo, I wrote the code here in the forum.... I correct it.
In reality I simplified it a lot but the concept is the same.

EDIT: Code adjusted
 
Last edited:
Upvote 0

Star-Dust

Expert
Licensed User
Longtime User
I don't know if I understand your problem correctly, but I don't think we can stop reading,
Rather I would create a buffer (cache) that accumulates the reads in a list or a FIFO stack until the 'traffic light' is green and I send them to processing.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
When I send to ESP, it resend back the response, NewData is called, then the code in the sub SendCommand will not continue after this line:
B4X:
Wait For (SendSocket(aCommand)) Complete (Success as Boolean)
I never see SUCCESS on the log, and no boolean return value returned to the main.

Maybe I need to call this to discharge all data arrived and continue the sub ?
B4X:
Wait For SocketAStreams_NewData (Buffer() As Byte)
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
The problem seem that when data arrive NewData is called (interrupting any other Sub execution) and the application next do dot return to call sub, but to the main.

ESP while connected always send data to Android over WiFi, every second send a Temperature message, even send the ESP serial log of a 3D printer.

When I upload a gcode file from android to ESP SD Card, I need to disable the Astream_NewData because I use an exact procedure wher Android send file size to ESP, ESP check if there is free space on SD card, if yes respond SPACEOK and start upload a file in chunks, if there is no space ESP send NOSPACE and Android abort upload.

Here I currently call WaitFor AstreamNewData and filter the incoming message until receive SPACEOK or NOSPACE and just reject any other message, but thi is not good, it sometime works, sometime fails.
 
Last edited:
Upvote 0

agraham

Expert
Licensed User
Longtime User
NewData is called (interrupting any other Sub execution)
An event cannot interrupt another Sub. All code in B4A runs on the single main thread. Any Sub has to run to completion and return to the message loop before the event can be serviced. And yes, this includes resumable subs which return to the message loop in the Wait For and are resumed when what they are waiting for completes. I suspect your incorrect model of what is happening is making you look in the wrong place for the problem.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Probably because I'm in Debug mode and every second arrive a temperature message and even other log messages, so it continue to loop Astream_NewData sub ?
Probably loop a long incoming quee messages ?
But even in Normal mode I do not see the SUCCESS on the log.

In Debug mode line by line when WaitFor SendSocket is called, the execution pass to Astream_NewData and never return back, continue to process it in a loop.

I used WaitFor for all instances, so I do not know because it does not work, Astream.write(buffer) return True if data was written, False if Quee is full and data cannot writed, note that even I do no use this:
B4X:
Dim ok As Boolean = SendCommand("MyCommand")
but this:
B4X:
Wait For (SendCommand("MyCommand")) Complete (Success As Boolean)
Maybe SendCommand signature need to be Resumable Sub instead of Boolean ?
 
Last edited:
Upvote 0

agraham

Expert
Licensed User
Longtime User
Your code above cannot be correct because SendCommand has an End Sub instead of an End If and if it is delcared as returning Boolean but is called by a Wait For then there will be an error above the Log pane and the Sub SendCommand line will be underlined in red and will not compile.

If you don't post your actual code then no wonder we can't help you.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Yes, sorry, the End Sub is a typo, now I will correct it, thanks you for it.

I can post the full main code (without 10+ classes) but it is a really complex project I worked for 4 ages every day, only Main is around 12.000 lines of code but there are a lots of comments I just retained until app is not finished.

On this part there is all interesting code that manage socket (but even views and other not related things), now I changed a bit some WaitFor and sub returns and I'm no able to know if packets are sent from socket (writed to AsyncStream quee).

I know even that is best put the socket management on a separate Service, but now it is on top of B4XPages.... and even here I've some questions next I will post on other threads. For a High battery consumption other questions... after I investigate it.

Please do me some time and I will post my app main code.

Many Thanks @agraham
 
Last edited:
Upvote 0

agraham

Expert
Licensed User
Longtime User
Yes, sorry, the End Sub is a typo, now I will correct it, thanks you for it.
There is no point 'correcting it'. I have no confidence whatsoever that that is your actual code as with that SendCommand declaration it won't compile. We don't want the whole project, just COPY (DON'T RETYPE) the ACTUAL CODE that is not working.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Maybe SendCommand signature need to be Resumable Sub instead of Boolean ?
Sure.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Ok, here a copy/paste of relevant code, just removed not related things,
all start when I press my styled button on sub btnYup_On that send a command to ESP that move the Y axis UP, this code do not know if message was sent, the SUCCESS log never called, PASSED log never show:
B4X:
Public Sub btnYup_On(Index As Int, Tag As String)
    Dim Msg As String = "G0 Y" & NumberFormat2(CurY + spFreeMoveSize.GetItem(spFreeMoveSize.SelectedIndex), 1, 4, 4, False) & " F" & Settings.Y_FEED   'G0_Speed
    ManualControl = True
    Wait For (ProcessSend(Msg)) Complete (Success As Boolean)
 
    If Success Then
        Log("SUCCESS, NOW WE PARSE THE MESSAGE")  ' THIS NEVER SHOW ON THE LOG, BUT MESSAGE IS SENT CORRECTLY, THE 3D PRINTER MOVE Y AXIS UP
        Parser.ParseLine(Msg)
        ProcessLine
    End If
End Sub

' This sub is used to do different things if USB or Socket is connected or just we use a simulation
' without command any 3D Printer, here for the test purpose only put our socket but originally there are
' some If to know if we use USB, WiFi or simulation. Similary to SendSocket ther is a sub SendSerial (not used here).
Public Sub ProcessSend(Msg As String) As ResumableSub
    Dim suc As Boolean
 
    If WiFiConn.Connected Then
              
        If ManualControl Then
            txtGCodeOut.TextColor = Colors.Green
            txtGCodeOut.Text = Msg
        End If
      
        Msg = "CMD: " & Msg

        Wait For (SendSocket(Msg)) Complete (Success As Boolean)
              
        Log("PASSED")

        suc = Success
    Else
        If ManualControl Then
            txtGCodeOut.TextColor = Colors.Red
            txtGCodeOut.Text = "NO WIFI OUT"
            If GeneralLog Then LogMessage("NO WIFI OUT")
        End If
        suc = False
    End If
          
    If ManualControl Then
        txtGCodeOut.Invalidate
        ManualControl = False
    End If
 
    Return suc
End Sub

Sub SendSocket(Msg As String) As ResumableSub
    If Not(WiFiConn.Connected) Or Msg.Length = 0 Then Return False
    If WaitingHotEndTempTarget Or WaitingHotBedTempTarget Then Return False
 
    Log("SOCK SND: [" &  Msg.Replace(CRLF, "(LF)") & "]")
 
    Dim Bytes() As Byte = Msg.GetBytes("UTF8")
 
'    Do While SocketAStreams.OutputQueueSize > 0
'    Loop
 
    Return SocketAStreams.Write(Bytes)
End Sub

Sub SocketAStreams_NewData (Buffer() As Byte)
    Dim rcv As String = BytesToString(Buffer, 0, Buffer.Length, "UTF-8")
    Dim newDataStart As Int = sbsockbuf.Length
  
    sbsockbuf.Append(rcv)  ' Collect data
 
    Dim s As String = sbsockbuf.ToString
    Dim start As Int = 0
 
    For i = newDataStart To s.Length - 1
        Dim c As Char = s.CharAt(i)
        If c = Chr(10) Then '\n
            Dim msg As String = s.SubString2(start, i) 
            ParseSocketMessage(msg)
            start = i + 1
        End If
    Next
 
    If start > 0 Then sbsockbuf.Remove(0, start)
End Sub
 
Sub ParseSocketMessage(received As String)
 
    If received.Length = 0 Then Return

    If received.StartsWith("LogMessage: ") Then
'       ..........
      
    Else If received.StartsWith("Progress: ") Then
'       ..........
      
    Else If received.StartsWith("Elapsed: ") Then
'          ..........
  
    Else If received.StartsWith("COMPLETED: ") Then
'       ..........
  
    Else If received.StartsWith("CMD: ") Then
'         .........

    End If
 
End Sub
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Can be useful put a Sleep(0) on SendSocket sub ?
Just make them all normal Subs and see if it works.
Just return any sub as Boolean ?
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
Why would you want to do that.? Have you tried what I suggested? All SendSocket does is an AsyncStreams Write which is not an asynchronous operation. By the way from looking inside AsyncStreams it seems that Write can have caused a blocking wait of up to 100 milliseconds if it returns False. as it waits that long to see if the queue empties enough for the new data.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
You are absolutely right, my honor. 👍👍

As your advices I set any sub signature to just Boolean, tried in Debug mode and seem to work very well 👌👌

Now I see both SUCCESS and PASSED log and I can know if data successfull writed to the stream.
Now seem that Astream_NewData wait until my code finish to be executed, I need to test it better.
And removed all WaitFor.

Because I use the ProcessSend function to send the message to USB (if connected) instead of socket, I suppose can use it the same way?
I suppose yes because I checked now and already return Boolean, not Resumable Sub.

This can help a lot or create problems while I upload a file, here Client (Android) and Server (ESP32) exchange some infos, the Client send file size, the Server check for space and respond true or false, if true Client send a file, if false abort. Here even Client pass some variables to Server, like file name, print settings etc.., like it is a single program.

I will test this part where I had some sync problems (Client/Server) I wanted to fix with some Sleeps, and then inform you here if it is working better now.

Here my test code I've used to transfer files:
https://www.b4x.com/android/forum/t...p32-micro-sd-card-in-plain-tcp-no-ftp.142224/

Your advices worked well, many Thanks for help you yold me.
 
Last edited:
Upvote 0
Top