LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Looking for help with Serial Communication, Events, and State machines

Solved!
Go to solution

Hi Folks,

 

(sorry for the long post in advance)
I am using LV 8.6 and building an application to use for debugging wireless hardware targeting Win 7 - Win 10. I have found in testing using state machines is the simplest way for me to send multiple commands to a serial port but that this is not enough when implementing user interfaces.


I also started using events to get user feedback. Against the advice of everything I have seen on the forums, I used state machines inside of an event structure just because it was simpler to implement for me at the time and I honestly am not very good at more complex design patterns. I just haven't had enough practice but I have taken LV training going over some other design patterns - but it has been a long time.

 

I was looking at this, for example, https://labviewwiki.org/wiki/Design_Patterns_Overview

 

I'm guessing - the correct thing to implement is probably Queued State Machine & Event-Driven Producer / Consumer.


One issue that I've come across is that the wireless device, In this case, a WIFI embedded module that acts as a WIFI Access point and a second WIFI station (multiple instances of the same VI or multiple executables) will boot up with a multiline header if reset (~1216 characters with each line terminated by a LF character).


After this, there are commands I can issue (to config modules into Acess points or stations within the WIF system) that are single-line commands that will also some times respond with multiple lines with termination characters.


I've broken all kinds of serial port rules (using too many closes and "bytes at port" for example but have tried to maintained some level of neatness and error handling flow control) as well as using Typedefs with the state machines (Although I do use multiple state machines with their own defined typedefs).


I'm looking at overhauling things. I think I'm leaning on the design pattern where you use Events to handle user interaction, state-machines to do the work, and serial communication port with looking for the termination char and reading more bytes than needed.


I tried a starting point with just the serial port and event but got stuck.


My main problems center around wanting to receive asynchronous reporting that is received on the serial as well as being able to handle user input this is in a responsive fashion.


I found a possible starting point here but have made some error since it doesn't reliably work. When I turn my wireless module on I get only part of the header data, then if I cycle the power I will get the missing part of the header plus the whole header. I realize this was just a quick example put together but I was also wondering about how to handle error control when there are parallel loops - won't connecting between them cause the flow to no longer run in parallel?


https://forums.ni.com/t5/LabVIEW/serial-port-event/td-p/3189883
This is based on the example in the thread by crossrulz - simple read example
I tried to recreate the example removing the "bytes at port".

 

BTW - I saw a post by Ravens Fan and Dennis Knutson that talked about all the different serial port use cases but can't find it now if anyone knows where this is please let me know I need to bookmark it.


Please be kind, I feel like I need some serious LabVIEW interventionSmiley Happy


Thanks,
-SS



0 Kudos
Message 1 of 20
(4,946 Views)

Hmm.  I'm going to assume that your VISA device sends relatively short strings (say <1000 characters) terminated by a "Termination Character" (most commonly 0xA, <Line Feed>).  Let's also assume that you have set the TimeOut to one second (instead of the default 10, I think, seconds).

 

Consider a VISA Loop that has a "Read 1000 characters", with a test following for a Time-Out (done to allow you to "break out" of this loop when you need to do so, such as when the Program is ready to exit).  When this happens, you've got a response, so you send it (via a Queue or a Stream Channel) to a routine that parses the string and "does what needs to be done".  Here are two parallel loops, one waiting for something from the Serial Device, gathering the data, and handing it off to the other to "deal with it".  You could consider this a form of Producer/Consumer, with the VISA Listener being the Producer and the "Parser" being the Consumer.  I would envision the Parser feeding its information into, say, a Queued Message Handler to take the "appropriate action".

 

No Bytes at Port anywhere!  None needed.  Also no Event Structure in this part of the code -- the Producer (by its design) automatically "runs" when the VISA device sends a Termination Character (and that's all that it does).

 

Bob Schor

Message 2 of 20
(4,872 Views)

Hi Bob,

 

The initial header is 1216 characters and can potentially be longer since when you add services it lists them in the serial response. After this the responses are less than 1000 char.  I don't know if you were able to see the code so I will attach a screenshot (it is a pretty old version).

Serial Loop.png

I'm not clear on what the visa event inputs should be set to, I've tried a few configurations but would like to know what the best are.  The only other case in the top loop is a constant boolean "T" wire out on an event when Stop Value is changed.  I did not put any other cases in the bottom loop (I'm sure I need another here) and I don't know where to put the close (maybe in the event when I hit stop).

Thanks,

-SS



0 Kudos
Message 3 of 20
(4,848 Views)
Solution
Accepted by topic author ShotSimon

I'm going to put some people into shock here...Do use the Bytes At Port.  I haven't had much luck with the VISA Events, so what I do is use the Bytes At Port to see if any message has started.  I can then read a full line.  If there is no data (bytes = 0), then I use a short wait (50ms) before checking again.  This allows the loop to handle the stop button or messages coming from other loops fairly often while also keeping the serial port as a priority.

NOTE: The "0" case just wires the error and VISA Reference through along with a 50ms wait.



There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
Message 4 of 20
(4,826 Views)

Firstly, let me say I like Bob's idea to have a continuous producing loop that just monitors the Serial line and packages collections of data. I'd be slightly concerned about it not timing out between different "responses" or whatever you might be receiving, but if that isn't the case, it sounds pretty nice (and simple, so yay!)

 

Can you give some more detail about when this device (or pair of devices?) sends information along the serial port?

So far I can see:

  • At startup/reset (non-fixed length, but basically a bunch of stuff over multiple lines, ~1200 characters)
  • In response to a command you issue (and therefore know when it occurs), can also be multiple lines (perhaps you know the number of lines given the command?)
  • Periodically (you mentioned "asynchronous reporting") - what does this contain? How frequent is a "report"?

 

How do you plan to use the application? As in, when "debugging wireless hardware", what does that entail?

 

Sorry for all the questions with no answers (or even suggestions, yet). Hopefully with a better idea, I can reverse that balance 🙂

 


GCentral
Message 5 of 20
(4,821 Views)

@crossrulz wrote:

I'm going to put some people into shock here...Do use the Bytes At Port.  I haven't had much luck with the VISA Events, so what I do is use the Bytes At Port to see if any message has started.  I can then read a full line.  If there is no data (bytes = 0), then I use a short wait (50ms) before checking again.  This allows the loop to handle the stop button or messages coming from other loops fairly often while also keeping the serial port as a priority.

NOTE: The "0" case just wires the error and VISA Reference through along with a 50ms wait.


When I use "Bytes at Port", it is always in this manner.  Additionally, I read the timeout value and "fake" a timeout error after that time has elapsed because I don't think "Bytes at Port" times out.

Bill
CLD
(Mid-Level minion.)
My support system ensures that I don't look totally incompetent.
Proud to say that I've progressed beyond knowing just enough to be dangerous. I now know enough to know that I have no clue about anything at all.
Humble author of the CLAD Nugget.
Message 6 of 20
(4,806 Views)

@ShotSimon wrote:

Hi Bob,

 

The initial header is 1216 characters and can potentially be longer since when you add services it lists them in the serial response. After this the responses are less than 1000 char.


Are you saying that you get a string of 1216 characters that look like it is one (very) long line?  No <CR><LF> or other line breaks?  And how about the responses that are less than 1000 characters?

 

I normally use a Read of 1024 characters, just because asking for 2048 just seems silly, but if you really have a Humonga-String, why not?  The nice thing about this scheme is that (I believe) it "auto-blocks", that is, if no characters are at the port, this code takes (almost) no CPU time (or so I believe ...).

 

Bob Schor

Message 7 of 20
(4,800 Views)

First off thank you for all the responses so far.  I began with older serial libraries I had based on the simple read-write example. 

 

CrossRulz - I did implement the changes you recommended on the vi that I originally shared and this worked very well.  I have since discovered at the end of lines it's possible to get an unterminated ">" prompt that gets lost in the reading due to using the termination character LF (any way to use two termination characters?).  I would also be interested to see where and how you implement the fake timeout error.

 

Bob - great implementation idea, I just don't know how to implement this properly.

 

Cbutcher - to answer some questions:

 

Each command response is different lengths.  For example, there could be an SSID scan that sends back 30 lines, and another command to enable GPIO that sends back one.  All lines are terminated with the addition of the surprise ">" with no LF termination.  And this ">" doesn't show up in each response (fun I know).

 

Asynchronous reports are a single line that spits back the device address and the IP addresses of devices that are connected.  These happen every min or so.

"Net:\sDHCPv4s:\sClient\sIP=192.168.0.11\s\sClient\sMAC=00:03:7f:99:90:24\n"

 

The application that I currently have uses buttons to allow users to Setup an Access Point or Stations, which then runs a state machine to set up the access point (this holds the user interface since this is in the event) I realize this is not ideal and would like recommendations to fix this.

 

There are other commands I add again with there own state machines or individual command since some time I only need to send one command.

 

For example:

Start RX test, Stop RX test
Identify Device
Find available comports
Get sensor data, Transmit Packet - Sensor Data, Recieve sensor data
Start Bandwidth Test TX, Start Bandwidth Test Rx

 

Bob - Last question you asked,  I get a string of data with LF on each line that has a bunch of lines.  This end with three LF followed by a prompt ">".

 

Again Thanks!

-SS



0 Kudos
Message 8 of 20
(4,786 Views)

@ShotSimon wrote:

I would also be interested to see where and how you implement the fake timeout error.


I have just set the timeout to something short (500ms, at least long enough to read an entire message) and let the VISA Read time out.  It will return whatever was already in there.  You can choose to throw that away or add the CRLF yourself on the timeout.  I have done both methods.



There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
Message 9 of 20
(4,765 Views)

It may well be that this statement:


@ShotSimon wrote:

All lines are terminated with the addition of the surprise ">" with no LF termination.  And this ">" doesn't show up in each response (fun I know).


prevents this idea from working, but you could try using the ">" prompt character as your termination character. If you wanted separate lines after that, you'd need to split on the LF characters yourself (but that's simple enough).

Of course, you'd probably also need a fairly large number for number of bytes to read since you'd be getting the entire response (e.g. 30 lines in some cases) in one Read.

 

It would also depend on if you get a "new" prompt after an asynchronous report, which I fear you might not... 😕

 

During the start of your application, it might be possible to flush the VISA buffers and then use something like the "Identify Device" command you mention to ensure the reading is correctly set?


GCentral
0 Kudos
Message 10 of 20
(4,756 Views)