Android Question Is it a good practice use Wait For and Sleep inside a Timer event ?

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

I still control a 3D printer print from Android, in the Timer event _Tick I read gcode lines one by one and send to a printer,
the printer should respond with 'ok' acknowledge message.

Inside a Timer event I have more that one Wait For that call subs in the same module and even on other classes, eg. I have a SerialConnector class and a SocketManager class that is used to send feedback over wifi.

Before I send a command to a 3D printer I put ACK_OK variable to False and when I receive 'ok' I put it to True, inside a Timer I check if ACK_OK is True, if it is True I continue to read and send gcode lines until receive 'ok' and so...

The problem here is that if I put the timer interval to 50-100 milliseconds all works, when I try to decrease it I have a lots of miss packets from 3D printer and all works in a strange way.

I know that Wait For and Resumable subs looks like a return to the parent but the problem is that these probably cannot be used in a timer ?

it is best to use a Do While loop instead of Timers while use Resumable subs?

Here is how my Timer event looks.
Many thanks for clarifications

B4X:
Private Sub TimerLine_Tick
'    LogMessage("TimerLine_Tick:   ACK_OK: " & ACK_OK, True, False)
  
    If SerialConn.Connected = False Then Return
      
'    If Loader.LineNumber > Loader.NumberOfLines Then Return  ' VEDERE SE CI VUOLE
  
    If Loader.LineNumber <= Loader.NumberOfLines Then
          
'            Sleep(0)    ' <<<<<<<<<<<<< DA CONTROLLARE
          
        If ACK_OK And Not(PAUSED) Then
'        If ACK_OK Then
          
            Line = Loader.ReadNextLine
          
'            Wait For (LogMessage ("TIMERLINE READ LINE [" & Loader.LineNumber & "]: " & Loader.LastLine, True, False)) Complete (o As Object)
            Wait For    (LogMessage ("TIMERLINE READ LINE: [" & Loader.LineNumber & "/" & mNumberOfLines & "]  [" & Line & "]", True, False)) Complete (o As Object)
          
'            If Line.Length = 0 Then Return  'Continue
  
            If Line.Contains("M105") Then Line = Line.Replace("M105", "")  ' Preso dallo sketch di ESP
          
            If Line = "END" Or STOP_MOVE Then  'Or Paused Then
                If WAIT_ACK Then
                    If SHOW_GENERAL_LOG Then
                        Wait For (LogMessage("TimerLine (WAIT_ACK): Read <END> or STOP_MOVE", True, False)) Complete (o As Object)
                    End If
                Else
                    If SHOW_GENERAL_LOG Then
                        Wait For (LogMessage("TimerLine (NO WAIT_ACK): Read <END> or STOP_MOVE", True, False)) Complete (o As Object)
                    End If
                End If
          
                Wait For (LogMessage("DISATTIVO TIMERLINE", True, False)) Complete (o As Object)
                TimerLine.Enabled = False  ' <<<<<<<<<<<<<< FILE TERMINATO DISABILITA IL TIMER LETTURA LINEE
      
                Wait For (ShowComplete(DateTime.Now)) Complete (b As Boolean)
                Return
            End If
  
            Wait For (SocketManager.Send("CMD: LINE: " & Loader.LineNumber)) Complete (rtn As Boolean)
'            Log("INVIATO VIA SOCKET CMD: LINE: " & Loader.LineNumber)  
            Wait For (SocketManager.Send("CMD: READ: " & Loader.ReadBytes)) Complete (rtn As Boolean)
'            Log("INVIATO VIA SOCKET CMD: READ: " & Loader.ReadBytes)
          
            Dim LineNumber As Long = Loader.LineNumber
            If (LineNumber > 0) Then
                '            If(Loader.LineNumber Mod ProgressFactor = 0) And SHOW_PROGRESS_LOG Then SetProgress
                If (LineNumber Mod (LineNumber / 100) = 0) Then
                    Wait For (SetProgress) Complete (o As Object)
                End If
'            If SHOW_PROGRESS_LOG Then SetProgress      
            End If
      
            Parser.ParseLine(Line)
      
            If (Parser.Command.Length > 0) And (Parser.IsCommentLine = False) Then

                If SHOW_VERBOSE_LOG Then
                    Wait For    (LogMessage ("CURRENT COMMAND: [" & LineNumber & "/" & mNumberOfLines & "]  [" & Parser.Command & "]", True, False)) Complete (o As Object)
                End If                              
              
                If mCNCType = TYPE_CNC_PRINTER And Parser.Command <> "M105" Then
                    Wait For (SocketManager.Send("CMD: " & Parser.Command)) Complete (r As Boolean)
                Else
                    Wait For (SocketManager.Send("CMD: " & Parser.Command)) Complete (r As Boolean)
                End If      
          
                '''''                ProcessLine
                  
                Wait For (ProcessLine) Complete (succ As Boolean)
                If succ = False Then
                    Wait For (LogMessage ("TimerLine: ProcessLine: Cannot process line: " & Parser.Command, True, True)) Complete (o As Object)
                End If
                      
                If mCNCType = TYPE_CNC_PRINTER Then
                    If (Line.startsWith ("M104") Or Line.startsWith ("M140") Or Line.startsWith ("M109") Or Line.startsWith ("M190")) Then
  
                        Wait For (Beep (2200, 10)) Complete (rt As Boolean)
  
                        Dim temp As Int = Parser.Value ("S")
                        If (Line.startsWith ("M104")) Then
                            Wait For (LogMessage ("SET EXTRUDER TEMPERATURE TO " & temp & " C", True, True)) Complete (o As Object)
                        Else If (Line.startsWith ("M140")) Then
                            Wait For (LogMessage ("SET BED TEMPERATURE TO " & temp & " C", True, True)) Complete (o As Object)
                        Else If (Line.startsWith ("M109")) Then
                            Wait For (LogMessage ("WAITING EXTRUDER TEMPERATURE " & temp & " C ...", True, True)) Complete (o As Object)
                        Else If (Line.startsWith ("M190")) Then
                            Wait For (LogMessage ("WAITING BED TEMPERATURE " & temp & " C ...", True, True)) Complete (o As Object)
                        Else If (Line.startsWith ("M105")) Then
                            Wait For (LogMessage ("TEMPERATURE REQUEST" & temp & " C ...", True, True)) Complete (o As Object)
                        End If
                    End If
                End If
              
                If WAIT_ACK Then ACK_OK = False  'IMPORTANTE AZZERA ACK PRIMA DELL'INVIO. MESSO NELLA SUB Send PER INVIO SERIALE
                  
                If (Parser.Command <> "M105") Then
                      
                    Wait For (SendSerial (Parser.Command)) Complete (succ As Boolean)
                    If succ = True Then
                        Wait For (LogMessage ("TimerLine: Successfull sent to Serial: " & Parser.Command, True, True)) Complete (o As Object)
                    End If
                End If
          
                If (WAIT_ACK = False) Then
                    '----- DELAY -----
                    If (Feedrate < 50) Then Feedrate = 50
                    Dim dly As Long = (LastTravel * 64000.0) / Feedrate
                    dly = dly * 180 / 1000
                    If (Line.length > 0) And (Parser.IsCommentLine = False) Then  ' Real control withouth Acknowledge. We add a delay based On last travel
                        If SHOW_VERBOSE_LOG Then
                            Wait For (LogMessage ("Wait " & dly & " ms  (NO_ACK)       Feedrate: " & Feedrate, True, True)) Complete (o As Object)
                        End If
                        Sleep (dly)
                    End If
                End If
          
                If WAIT_ACK Then Return  ' FORSE NON SERVE
            Else
'                If SHOW_VERBOSE_LOG And Line.Length > 0 Then LogMessage("TimerLine (WAIT_ACK): COMMENT on line [" & Loader.LineNumber & " of " & Loader.NumberOfLines & "] -> " & Line, True, False)
                If Line.Length > 0 Then
                    Wait For (LogMessage("TimerLine (WAIT_ACK): COMMENT on line [" & LineNumber & " of " & mNumberOfLines & "] -> " & Line, True, False)) Complete (o As Object)
                End If
                Sleep (1)
'                Sleep(200)
            End If
  
        End If  ' END OF ACK_OK = TRUE
          
    End If  ' END OF Loader.LineNumber <= Loader.NumberOfLines
    
End Sub
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
The timer can tick again before the previous sub execution completed (only because this is a resumable sub).
You need to carefully handle such cases.

1. Using the index pattern: https://www.b4x.com/android/forum/threads/b4x-resumable-subs-and-the-index-pattern.111487/#content
2. If you don't want the timer to run again until the previous execution completes then change your code to:
B4X:
Do While True
 Try  
'....
 Catch
 '...
End Try
Sleep(100)
Loop

And make sure to only call this sub once.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Looks good @Erel, I have to see how adapt this to my use case:
B4X:
Sub B4XSeekBar1_ValueChanged (Value As Int)
   Label1Index = Label1Index + 1 'global Int variable
   Dim MyIndex As Int = Label1Index
   Do Until Label1.Text = Value
       If Value > Label1.Text Then
           Label1.Text = Label1.Text + 1
       Else If Value < Label1.Text Then
           Label1.Text = Label1.Text - 1
       End If
       Sleep(30)
       'Need to check after every call to Sleep or Wait For
       If MyIndex <> Label1Index Then Return
   Loop
End Sub
and put it inside a Do While loop ? Next when I read back all gcode lines and printing process finish I use Exit to exit the Do While ?

Note that I've managed the same project that works on Raspberry (with B4J), here I do not used Resumable subs at all, they works with timer, but with the Timer Interval set to 1 millisecond, when 3D printer prints something like curves or circles with a lot of lines read from file and sent to USB, the printer slow down, so I think While loop can help here.

I searched to mantain the WaitFor to call other subs that even use Wait For that even call other Resumable subs, but the USB _DataAvailable and Socket AStream _NewData I think cannot call a Resumable sub ? They should return before next command, so only when all executed from Resumable subs. Something like this:
B4X:
Private Sub Serial_DataAvailable (Buffer() As Byte)

    Dim newDataStart As Int = sb.Length
    sb.Append(BytesToString(Buffer, 0, Buffer.Length, Starter.charset))  ' ORIGINALE CORRETTA
 
'    If sb.Length > 0 Then Log("sb: " & "[" & sb.ToString.Replace(Chr(10), "*") & "]") ' DEBUG
 
    Dim s As String = sb.ToString
    Dim start As Int = 0
 
    For i = newDataStart To s.Length - 1
 
        Dim c As Char = s.CharAt(i)
        If i = 0 And c = Chr(10) Then '\n...
            start = 1    ' might be a broken end of line character
            Continue
        End If
 
        If c = Chr(10) Then    ' \n
            If SubExists(mModule, mEventName & "_Received") Then
'                Log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<: [" & s.SubString2(start, i) & "]")
                ''''''                CallSubDelayed2(mModule, mEventName & "_Received", s.SubString2(start, i))
                CallSub2(mModule, mEventName & "_Received", s.SubString2(start, i))
            End If
 
            start = i + 1
        Else If c = Chr(13) Then    ' \r
            If SubExists(mModule, mEventName & "_Received") Then
                '''''''                CallSubDelayed2(mModule, mEventName & "_Received", s.SubString2(start, i))
                CallSub2(mModule, mEventName & "_Received", s.SubString2(start, i))
            End If

            If i < s.Length - 1 And s.CharAt(i + 1) = Chr(10) Then '\r\n
                i = i + 1
            End If
            start = i + 1
        End If
 
    Next
 
    If start > 0 Then sb.Remove(0, start)
 
    Counter = Counter + 1

End Sub

Note that here CallSub2 call SerialConnector_Received event in the Starter service where SerialConnector class is declared and initialized.
But this is Resumable sub too because it uses some Wait For to call other Resumable subs, in this case is good CallSub2 or I shoud use Wait For here ?
The same is for SocketConnector. Both send and receive.

Many thanks for advices
 
Last edited:
Upvote 0
Top