Android Question Wait For event multiple times

warwound

Expert
Licensed User
Longtime User
I have a SerialPort library that raises the event:
B4X:
BytesRead(Bytes() As Byte)
The event is raised multiple times after a command has been sent to the SerialPort.
That is - a single command's output usually results in the BytesRead event being called multiple times.

Will this example code Wait For the BytesRead events multiple times or only once?

B4X:
Public Sub SendCommand2(Command As String)
    SerialPort1.PrintLn(Command)
    Wait For SerialPort1_BytesRead(Bytes() As Byte)
    Dim BytesAsString As String=BytesToString(Bytes, 0, Bytes.Length, ENCODING_UTF8)
    SerialBuffer.Append(BytesAsString)
    '    TODO does SerialBuffer now contain the full response for the command?
    '    or do we need to wait for more bytes?
End Sub

Presumably it'll Wait For the event just once, so I need to do something like this:

B4X:
Public Sub SendCommand2(Command As String)
    Dim BytesAsString As String
    SerialPort1.PrintLn(Command)
    '    TERMINAL_MODE_PROMPT_SHELL denotes the end of the serial response for the command
    '    BytesRead will not be raised again
    Do While Not(BytesAsString.Trim.EndsWith(TERMINAL_MODE_PROMPT_SHELL))
        Wait For SerialPort1_BytesRead(Bytes() As Byte)
        BytesAsString=BytesToString(Bytes, 0, Bytes.Length, ENCODING_UTF8)
        SerialBuffer.Append(BytesAsString)
    Loop
    '    TODO process SerialBuffer
End Sub

Is that correct and the second code snippet is the way to go?
 

agraham

Expert
Licensed User
Longtime User
Presumably it'll Wait For the event just once
I think so. Each WaitFor is packaged as a small class instances so each is independent and is discarded once the event has occurred so I think the second snippet is needed.

Serial comms is always more difficult than people think once you take the possibility of non-ideal circumstances happening - like your TERMINAL_MODE_PROMPT_SHELL character being corrupted in transmission. probably resulting in a permanent hang in your code as written!
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Alternatively you could use the old method of a separate sub instead of the wait for. It would be called as many times as necessary.
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
Serial comms is always more difficult than people think once you take the possibility of non-ideal circumstances happening - like your TERMINAL_MODE_PROMPT_SHELL character being corrupted in transmission. probably resulting in a permanent hang in your code as written!
Alternatively you could use the old method of a separate sub instead of the wait for.

Currently my code has a single 'traditional' callback sub:

B4X:
Private Sub SerialPort1_BytesRead(Bytes() As Byte)
    Dim BytesAsString As String=BytesToString(Bytes, 0, Bytes.Length, ENCODING_UTF8)
    SerialBuffer.Append(BytesAsString)
    If BytesAsString.Trim.EndsWith(TERMINAL_MODE_PROMPT_SHELL) Then
        Select LastCommand
            Case COMMAND_GENERIC_TO_SHELL_MODE
                '    process command output
                
            Case COMMAND_RESET
                '    process command output
            
            Case COMMAND_SI
                '    process command output
            
            Case Else
                '    process command output
                
        End Select
    End If
End Sub

When it works it works but if the serial output for a command is not exactly what my code expects then the code generally fails and hangs.
The single callback sub can't recover from unexpected serial output - and it can't recover if the serial connection becomes disconnected.
To recover it would need to send additional serial commands and then the sub needs to handle the output from the additional commands.
The sub then becomes a nightmare to maintain.

My plan is to create one sub for every serial command I need to execute.
Each sub will wait for the SerialPort1_BytesRead(Bytes() As Byte) event and the single 'traditional' callback sub will be removed.
Within the wait for code block of each method I can catch problems specific to that command, have a timeout and better handle problems - while still having readable and maintainable code :cool:.
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
Serial comms is always more difficult than people think once you take the possibility of non-ideal circumstances happening - like your TERMINAL_MODE_PROMPT_SHELL character being corrupted in transmission. probably resulting in a permanent hang in your code as written!
I've spent days working on this now.
The problem:
I send a command over serial to a UWB module.
The serial output should contain either ": ok" or ": failed".
But often the serial output just echoes part of the command which was sent and then everything hangs.

I then noticed that any command which contains a space or any command more than 6 characters in total length fails and hangs.

I suspected a character encoding issue, where my text command converted to bytes which are written to the serial port and not correctly encoded or decoded.
This was not the problem.

I then suspected my OutputStream which writes the bytes to the serial port was not flushing all bytes to the serial port, so only part of a command was being sent to the UWB device.
This was not the problem.

I then suspected an underlying problem with the serial port library I'm using - this library is built into the custom android device's firmware, I have no documentation for it.
This was not the problem.

By now I'd googled endlessly for help, but then found this page:
https://forum.qorvo.com/t/issue-with-uart-shell-mode/3696
I am unable to send any commands with a space to the DWM1001
And the response:
shell is intended to be used with human and not with software. So in some previous thread is recommended to add cca 10ms delay between characters.
That is: introduce a small delay between sending every byte of the command.

The text serial mode of the UWB module is intended to handle human input, not streams of many bytes sent (rapidly) by code.
So I updated my code to send a byte, wait 10 millis then send next byte etc.
Success all of my commands now work as expected.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Simple and understandable, once you know.
 
Upvote 0
Top