custom serial protocol


mgtiger

Recommended Posts

I'm trying to create a custom serial protocol to receive unpolled data that is tagged. The format is as follows:

ID,0721577657,MO,4250,TI,35317.343715,BV,12.3,LE,0.1000,VE,0.1225,FL,0.001555

I need to place each piece of data based on the preceding 2 character tag. The amount and order of data will vary depending on the device configuration, so I'd like to search for the tag and capture the data if there. It seems that I need to use some combination of the parse function and find function, but I haven't been able to nail it down. Thanks for your help.

Link to comment
Share on other sites

Like many things in DAQFactory there are several ways to do this depending on your specific needs. The easiest is to create a sequence and use the ID's to name the channels. Lets assume you create a serial device called MySerial with a NULL Protocol, and your data actually has a carriage return (13) at the end of each line:

private string datain
device.MySerial.Purge()
while (1)
   try	  
	  // read a line:
	  datain = device.myserial.readuntil(13)
	  //parse into an array of strings:
	  datain = parse(datain,-1,",")
	  // now cycle through:
	  for (private x = 0, x < numrows(datain), x+=2)
		 try
			execute(datain[x] + ".AddValue(" + datain[x+1] + ")") 
		 catch()
		 endcatch
	  endfor
   catch()
   endcatch
 endwhile

This is one of the few cases where the infinite while loop does not need a delay(). This is because the readuntil() will delay itself (or timeout) until the carriage return is received.

Anyhow, what I did was simply read the line, use the -1 argument for Parse() to split it into an array of strings (which is faster than repetitively calling parse()), then I use the 2 character tag as the channel name to add the value to it. So, given your line, you end up doing ID.AddValue(0721577657) for the first iteration, MO.AddValue(4250), etc. So, all you have to do is create a channel called ID, or MO, or TI with Device Type "Test", and a Timing of 0 (other parameters don't really matter) and data will go there. Since I enclosed the execute() call inside a try/catch block, if the channel doesn't exists, the data goes nowhere and it just moves on to the next in line.

Now, if you want to have names that are longer than two letters for your channels, there are again several ways to change the above to achieve that. A switch/case chain instead of the execute is one way and probably easiest if you only have a few possibilities:

private string datain
device.MySerial.Purge()
while (1)
   try	  
	  // read a line:
	  datain = device.myserial.readuntil(13)
	  //parse into an array of strings:
	  datain = parse(datain,-1,",")
	  // now cycle through:
	  for (private x = 0, x < numrows(datain), x+=2)
		 switch
			case (datain[x] == "ID")
				Identifier.AddValue(strtodouble(datain[x+1])
			case (datain[x] == "MO")
				Motion.AddValue(strtodouble(datain[x+1])
		  endcase
	  endfor
   catch()
   endcatch
 endwhile

Just keep adding case() statements for the id's you want. You could also use search() to find specific identifiers in the parsed array, but then the code gets hard to read for little real benefit. I'd only do that if the you had 100 different identifiers and you only cared about a couple of them.

If you have a fair number of channels you'd like to do, or you want to be able to change it dynamically, you could use a sort of lookup table:

private string datain
private string IDs = {"ID","MO","VE"}  // etc...
private string ChanNames = {"Identifier","Motion","Velocity"}

device.MySerial.Purge()
while (1)
   try	  
	  // read a line:
	  datain = device.myserial.readuntil(13)
	  //parse into an array of strings:
	  datain = parse(datain,-1,",")
	  // now cycle through:
	  for (private x = 0, x < numrows(datain), x+=2)
		 private index = search(IDs == datain[x])
		 if (index == -1) 
			continue // not found 
		 endif
		 execute(ChanNames[index] + ".AddValue(" + datain[x+1] + ")") 
	  endfor
   catch()
   endcatch
 endwhile

Link to comment
Share on other sites

thanks for the help. My problem however, is that there are no carriage returns in the string. I had seen examples of how to extract data using the "readuntil(13)" line, but I've been unable to figure out what to use in this continuous comma delimited string. The output is literally:

ID,0721577657,MO,4250,TI,35317.343715,BV,12.3,LE,0.1000,VE,0.1225,FL,0.001555

with no other characters.

Link to comment
Share on other sites

You are sure? Are you using hyperterminal or the DAQFactory monitor to determine this. Only DAQFactory will show you the actual control characters. If there is no CR, what determines the end of a line, or for that matter the start of the next line? Does it always start with ID? Whoever designed your hardware is being pretty lame by not adding a CR at the end of an ascii string. One extra character on top of so many per line doesn't seem worth the added difficult in parsing the line from the other side.

Link to comment
Share on other sites

I don't have the equipment with me to test yet, but I'm reading directly from the manual. It shows the example string I posted earlier and explicitly states "there are no carriage returns in the text string".

As to your other question, no the string does not always start with ID. "Parsing routines for this output string should search by type identifier instead of depending on the position of the string. The order of the fields is subject to change based on which parameters are turned on/off.

Link to comment
Share on other sites

Its very hard to build protocol drivers without having the hardware because the manual is never clear enough. The engineers that write it try, but almost never get the description perfect. For example, your quoted description does not state if its simply a continuous stream of comma delimited values, or you get a value followed by an identifier without delimiter. You example string makes me think that maybe you get the second, but my guess is that you get a continuous stream of comma delimited values. If so, then just do readuntil(',') twice, after making sure the first is not a number (just do strtodouble() on it and see if its NaN() or not), then look at the first part for the identifier and the second part for the value. Repeat continuously.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.