serial communication in a sequential way

Hi,

I’m working on an embedded device that requires using AT commands to do settings and communicate with the board. So I hope to send several commands one by one and base on the response to determine what the next command will be sent out. For example,

send out “+++” to start AT command mode, if the feedback is “OK” then keep going, if the feedback is “(null) or something else” then resend “+++”.

I did find a discussion here, https://forum.xojo.com/12492-need-a-simple-serial-terminal
but it seems it’s too long ago and I can’t see the example by either using data available event or timer with serial.poll methods.

I also find something here, https://forum.xojo.com/16761-serial-rs232/0
@Jon Ogden provides a do…loop

[code]Serial1.Write(SomeCommand)

Do
Serial1.Poll
Loop Until Serial1.LookAhead.Instr(Some_Response_I_Want) <> 0

Dim ReadString as String = Serial1.ReadAll[/code]

I tried but for some reason, I always get in the infinite loop and the GUI just hang.

I understand it’s not like in the embedded C programming, you can set a timer and send protocol one by one if something wrong just resends or throws out an interrupt or similar. So what’s the best way in Xojo to do the similar job?

If anyone could help I would appreciate that.

Do
Loop until ?entervalue?

You loop always, serial is event based. No need to poll, use the DataAvailable event.

Or check for the value inside the loop and use a boolean flag after Until

[quote=350813:@Derk Jochems]Do
Loop until ?entervalue?

You loop always, serial is event based. No need to poll, use the DataAvailable event.

Or check for the value inside the loop and use a boolean flag after Until[/quote]

I need to write one thing wait and then parse the response and based on that, execute other code, etc. That’s the AT commands I try to execute. A typical process is following,

sender: +++
receiver: OK //the AT mode Starts
sender: ATOA //what’s your operation provider
receiver: VZW //it’s verizon wireless
sender: ATDL###.###.###.### // change the destination port to ###.###.###.###
receiver: OK
sender: ATWR //write the register
receiver: OK
sender: ATAC //apply changes
receiver: OK
sender: ATCN // exit AT mode
receiver: OK

I need to base on the feedback from the target board to decide what code I should execute or run. And from time-wise, these responses should take no more than 500 ms. In that case, the AT mode will end automatically or we should consider it’s timeout.
I know, usually, the serial is event-based, you can have a data available event handler to handle the response, but in my case how to do that and more specifically, how can I debug the code with this event? Can you give an example or point me to it?

I don’t really understand the problem. Can’t you just use an IF condition in the DataAvailable event?

[code]If receivedText = “OK” Then
Send(“ATOA”
Else If …

End If
[/code]

Or do I miss something?

Seems to me you need a state engine.

[code]Public Sub sendCommand()
Select Case State
Case 0
me.Write("+++")
Case 1
me.Write(“ATOA”)
Case 2
me.Write(“ATDL” + Port)
Case 3
me.Write(“ATWR”)
Case 4
me.Write(“ATAC”)
Case 5
me.Write(“ATCN”)
End Select

me.Write(EndOfLine)
State = State + 1

End Sub

Sub DataAvailable() Handles DataAvailable
Dim s As String = me.ReadAll

If s.Left(2) = “OK” Or s.Left(3) + “VZW” Then
sendCommand()
End If

End Sub

[/code]

Where State & Port are properties of a sub classed Serial Socket.

You might have a look on Roger Meier’s Freeware CoolTerm serial port terminal written in Xojo http://freeware.the-meiers.org/

I have struggled mightily trying to get my head around how to best handle synchronous communications in Xojo. I had been using the method you attribute to me earlier. But that just would not always be smooth and you could get into an infinite loop like you have had.

The thing I have discovered is to let the events do their thing. Write to the socket/port and then analyze the data received and take action after that. Wayne is right when he says to use a state engine.

I’ve even gone a step further and am now just writing all my desired commands to the port and then in the DataAvailable event using a series of RegEx functions to select the data that I want to process. That works quite well actually.

But you need to know what to send based on what was received. So the state engine is the way to go. Analyze what you have received. Branch to code based on that and then write the new command.

Just let the events take care of themselves. It’s so much easier than trying to shoehorn an event driven asynchronous object into being a synchronous object.

[quote=350949:@Jon Ogden]I have struggled mightily trying to get my head around how to best handle synchronous communications in Xojo. I had been using the method you attribute to me earlier. But that just would not always be smooth and you could get into an infinite loop like you have had.

The thing I have discovered is to let the events do their thing. Write to the socket/port and then analyze the data received and take action after that. Wayne is right when he says to use a state engine.

I’ve even gone a step further and am now just writing all my desired commands to the port and then in the DataAvailable event using a series of RegEx functions to select the data that I want to process. That works quite well actually.

But you need to know what to send based on what was received. So the state engine is the way to go. Analyze what you have received. Branch to code based on that and then write the new command.

Just let the events take care of themselves. It’s so much easier than trying to shoehorn an event driven asynchronous object into being a synchronous object.[/quote]
Thank you for your response. I guess I do not quite understand the state engine part. It seems you have done this successfully and smoothly. Could you give me some sudo-code example?

[quote=350869:@Wayne Golding]Seems to me you need a state engine.

[code]Public Sub sendCommand()
Select Case State
Case 0
me.Write("+++")
Case 1
me.Write(“ATOA”)
Case 2
me.Write(“ATDL” + Port)
Case 3
me.Write(“ATWR”)
Case 4
me.Write(“ATAC”)
Case 5
me.Write(“ATCN”)
End Select

me.Write(EndOfLine)
State = State + 1

End Sub

Sub DataAvailable() Handles DataAvailable
Dim s As String = me.ReadAll

If s.Left(2) = “OK” Or s.Left(3) + “VZW” Then
sendCommand()
End If

End Sub

[/code]

Where State & Port are properties of a sub classed Serial Socket.[/quote]
I don’t understand the state engine part you mentioned in your reply. is “sendCommand()” a method under serial class or something else? To do this, I have to subclass a serial socket and define a custom method of send command and define the properties of port and state, right? It’s the first time I try to sub classed a xojo default class.

Sure. Here’s some actual code that I use to parse data received from a TCPSocket. It is similar to RS-232. When the DataAvailable event of the socket fires, I call a timer and this code below is executed in the timer action event. Greg O’Lone suggested using the timer.

I have this code in the DataAvailable Event of the socket:

MySockData = MySockData+t.ReadAll(Encodings.ASCII)
DataAvailableTimer.Mode = Timer.ModeSingle

MySockData is a proper of my object.

Now here is some of the code from the timer. Basically I dump a bunch of commands to a device to get its status, information, etc. Here is how I analyze it. It will be meaningless to you in large part, but I’m showing you how you can basically take the data you have received and parse it or look for specific information. Then once you find the information, you act on it. That’s the purpose of a state machine.


Dim rx As New RegEx
Dim rxOptions As RegExOptions = rx.Options
rxOptions.LineEndType = 4
Dim match As RegExMatch
Dim matchPosition as Integer


// Code to look for firmware version

rx.SearchPattern = "(?msi-U)^([A-Z]\\x20?(?:\\d+\\.){1,2}\\d*\\x20?[A-Za-z]*.*)\\R+
```
"

match = rx.Search(MySockData)

If match <> Nil Then
  FirmwareVersion = match.SubExpressionString(1)
  Dim fw() As String = FirmwareVersion.Split(EndOfLine)
  FirmwareVersion = fw(0)
End If

//
// Code to look for RS232 data in the parameter dump

rx.SearchPattern = "(?msi-U)(soip_type2=[yn])[\\r\
]+(soip_guest_on=[yn])"

match = rx.Search(MySockData)

If match <> Nil Then
  Try
    mGuestMode = If (match.SubExpressionString(2).Right(1) = "y", True, False)
    mType2 = If (match.SubExpressionString(1).Right(1) = "y", True, False)
    mGotGuestMode = True
    'system.debuglog"Got RS232 Mode in Param Dump for Device "+Me.DeviceName
    RaiseEvent RS232ModeUpdate
    NotificationCenter.send("RS232ModeUpdated",RS232Mode,Self)
  Catch
    'RS232Mode = ""
  End Try
End If

// Code to look for baud rate in the parameter dump

rx.SearchPattern = "(?msi-U)s0_baudrate=([0-9]+)-([5678][ENO][12])"

match = rx.Search(MySockData)

If match <> Nil Then  // We have RS232Settings
  
  Try
    RS232Values(0) = match.SubExpressionString(1)
    RS232Values(1) = match.SubExpressionString(2).Uppercase
    RaiseEvent RS232SettingsUpdate
    NotificationCenter.Send("RS232SettingsUpdated",RS232Values,Self)
  Catch
    RS232Values(0) = ""
    RS232Values(1) = ""
  End Try
End If

// Code to look for the web name

rx.SearchPattern = "(?msi-U)webname=(.*)"

match = rx.Search(MySockData)

If match <> Nil Then
  Webname = match.SubExpressionString(1)
End If


// Code to look for the subnet mask

rx.SearchPattern ="(?msi-U)netmask=((?(DEFINE)(?'octet'25[0-5]|2[0-4]\\d|[01]?\\d{1,2}))\\b(?'octet1'(?&octet))\\.(?'octet2'(?&octet))\\.(?'octet" + _
"3'(?&octet))\\.(?'octet4'(?&octet)))\\b"

match = rx.Search(MySockData)

If match <> Nil Then
  SubnetMask = match.SubExpressionString(1)
  'system.debuglog "Got SubnetMask for "+me.DeviceName
End If

SendCommand is the method that Wayne created as part of the customer subclass. It analyzes the received data and takes appropriate action. That’s a state machine. Look at the data that has come in. If it matches the desired criteria then act on it. If not, ignore it and wait for the next chunk of data, etc.

Technically, that’s not a state machine. You’re acting on the data you received alone. A state machine acts on the data you most recently sent. It says, “I’m expecting such and such in response, what did I actually get?” It evaluates the response based on some expected value, based on the “state” of the conversation.

For example, if I’m talking to a modem, the first thing I send is “AT”, and I expect in response “OK”. If I get anything else, there is a problem. However, “OK” could be a valid transmission, so I don’t know based just on the data received what’s going on. So a State Machine says, Set the current state to “Initiating Conversation” and then send an AT command. When the data comes back, I have to first check the state of the conversation before I can evaluate the data received.

select case State

case "Initiate Conversation"
    if data = "OK" then
        State = "Conversation in Progress"
        me.Write "Hello There"
    else
       State = "Error"
    end

case "Conversation in Progress"
    if data = "OK" then
        me.Write "I'm glad you agree!"
    end

end case

[quote=355865:@Tim Hare]Technically, that’s not a state machine. You’re acting on the data you received alone. A state machine acts on the data you most recently sent. It says, “I’m expecting such and such in response, what did I actually get?” It evaluates the response based on some expected value, based on the “state” of the conversation.

For example, if I’m talking to a modem, the first thing I send is “AT”, and I expect in response “OK”. If I get anything else, there is a problem. However, “OK” could be a valid transmission, so I don’t know based just on the data received what’s going on. So a State Machine says, Set the current state to “Initiating Conversation” and then send an AT command. When the data comes back, I have to first check the state of the conversation before I can evaluate the data received.

[code]
select case State

case “Initiate Conversation”
if data = “OK” then
State = “Conversation in Progress”
me.Write “Hello There”
else
State = “Error”
end

case “Conversation in Progress”
if data = “OK” then
me.Write “I’m glad you agree!”
end

end case
[/code][/quote]

True. However, in my case I had a state machine but it’s actually faster to evaluate whatever data comes into the socket. For example if you send AT, the first data received event may only get O and not OK. So then you have to wait another cycle for the full response. Then send your next command etc.

Instead, what I do is send all my commands at one time and let all the TCP buffers figure it out. I can then process multiple chunks of data in one shot and not wait until a specific chunk is complete before issuing the next command and sending it out.

Sometimes, you may need a state machine if you really do need to process and do one thing in one case and another thing in an opposite case. But I bet many times, that’s not needed. So instead just send what you need to send and let the data come back and process it as it comes in. Once I allowed myself to do that, my world got much easier…

Thank you all for the help. @Jon Ogden @Tim Hare and @Wayne Golding

I finally like Jon did, put a timer inside of DataAvailable event and put the state engine code into the timer. Now it works quite smoothly as I expected. Tim has a very good point to my case, I will modify my code after I figure out the state transition sequence.

Again, thank you very much!