PDA

View Full Version : 2 questions regaurding string sizes - evolving to formatting variables



Joms
07-21-2009, 09:52 AM
I have a string that consists of the following...

(space)(space)(space)21900(space)(space)(space)(sp ace)00

I am trying to remove the data after the 21900. That brings up two additional questions...

1) When I use the TV_Text object to display this string is displays just like I typed above, however when I debug it using FDS Object it will debug as a weird 4 digit number.

2) I am also trying to use this string to compare to another decimal number. example, if the number is over 5000, activate an output. But it appears that the number is stored as a string and not a decimal. Am I correct on this?


I know I do not have a complete understanding of variables on the prop. I have the Prop Manual next to me and from my reading I tried changing the size of the variable, but that did not fix the problem. (When I say size I mean the 8 number... VariableName[8])


Post Edited (Joms) : 7/21/2009 11:03:03 PM GMT

Mike Green
07-21-2009, 11:37 AM
1) It may in fact not be what you think it is. In particular, the spaces may not really be spaces, so TV_Text may treat the characters one way while your PC (through FDS) may treat them another way.

2) Yes. The number is stored as a string

What you show in your message is a 14 byte string that occupies 15 bytes (including the zero byte at the end). You will need at least 15 bytes to store it. If you don't have that much, the string will spill over the next variable(s) in memory.

Keep in mind that Spin really doesn't have string variables. It has string constants that are represented by their starting address, but no built-in string types. There are two built-in functions, STRSIZE and STRCOMP that can deal with strings to find their length and compare two of them for equality, but that's it. Everything else that deals with strings is constructed out of other stuff.

Joms
07-21-2009, 11:45 AM
Ok... I think I am starting to figure this out now... Thanks for your help... But does bring up a few more questions...

The spaces actually are a hex 20 which is a space. How could I write a line that would delete the last 6 bytes? Or do I just have to over-write them with something else?

When I debug that number I would expect to get 21900 but I get 4928 as a decimal or 1340 as a hex... I assume that would be because I have not removed the extra 6 bytes?


EDIT -
Also, what does the @ due when I use it in front of the string? When I look in the manual (on page 278) it says that it is the 'Symbol Address Operator'. Does this mean I can type BYTE[@StringName] := $0, where BYTE is the number of the position the byte is in, and zero would be what I want to set the byte to? Would be setting it to zero the same as deleting it?

Sorry for all the questions, just trying to actually learn these strings as I have never really understood them completely, but really would like to as it would help fix a lot of problems I think...

Post Edited (Joms) : 7/21/2009 4:04:02 AM GMT

Mike Green
07-21-2009, 11:56 AM
What do you mean by "debug that number"?

Remember that a string is represented by the address of the first byte. Perhaps $1340 is the address of your string.

The question is not how to delete the last 6 bytes, but how to delete extra stuff that may be there if the number isn't 21900. Also, where do the spaces come from? How do you get the string in the first place?

Most string manipulation in Spin is done one byte at a time. For example, I'd write a routine that would start at the first byte of the string and increment the address of the first byte until that byte was non-blank. I'd save that address for later, then increment the address until the first non-digit was found and replace the non-digit character with a zero byte to mark the end of the string. I'd use the saved address as the actual address of the string since it points to what part I want.

w8an
07-21-2009, 11:56 AM
To drop the last six characters, couldn't you just re-terminate the string with something like:

stringname[8] := 0

Joms
07-21-2009, 12:19 PM
I was just working with something like what w8an was talking about and couldn't make that work properly.

The string is actually input from a scale I am reading data from. I will attach the code and maybe that will help with the explanation. And I think you are right about the $1340 being the first byte of the string. What I am trying to do is get the actual data off the string.

Basically I can make it display properly on the monitor (TV_Text) but I would like to compare it, for example, if it is over say 5000, activate a light or something. So I have been just trying to debug as a test before I try to program the compair line.

Mike Green
07-21-2009, 12:38 PM
Your source program doesn't help much. I guess you're looking for some kind of prefix, then putting the next characters up to a carriage return into a string. In any event, there's too much missing (like GUI and Heartbeat).

One important comment ... You're calling Main recursively for some reason. That will eventually cause the program to run out of stack memory and die. Maybe you should use a REPEAT statement instead.

Do you actually know what data you're supposed to receive? Do you know why you're getting the spaces? That's the first thing you should investigate.

Joms
07-21-2009, 12:48 PM
Mike, your a Genius! I was actually having another problem I have been trying to work on where it would re-boot after a period of time. I was leaning twards a power supply issue and trying to fix that when instead I should have used repeat there. Thanks for catching that for me...

As for the data... The data with the spaces is the way the scale outputs the data. I actually just like the string time out each time because I really can't use a delimiter like a carriage return. The data keeps repeating over and over... Attached is a sheet that shows exactly how it is sent out of the scale...

I just typed this up and it is probably a lot easier to understand...

Mike Green
07-21-2009, 12:56 PM
Do you have information about how the values are presented as the scale measures whatever it measures? Are the spaces replaced by significant digits? Is there another carriage return later in the string or does it always time out where you indicated? It's really important to characterize the format of the data under a variety of conditions. Your "data.pdf" document really doesn't say much.

Joms
07-21-2009, 01:01 PM
Really I don't have too much information about the scale. I made that document just by analyzing the data coming out of the scale. This is what I know...

1. The string is consistant in length every time
2. The part in Yellow is the weight that I am trying to capture and work with
3. The part in blue just changes if there is a negative number.
4. What is happening in my current programming is I am waiting for the (13,2,49) combination at which time I start inputing the data. The leading (20,20,20,30,30) is actually showing up at the end of my string because it is coming back around until it gets to the 13 again. Because there is really no delimiter in this system I am trying to only input a string of a certain length, say 8 bytes in this case.

localroger
07-21-2009, 09:29 PM
Joms, is this Toledo format continous output? If so I've written parsers for it in just about every language known to Man.

My advice for parsing it, which has worked for me in everything from VB to Stamp BASIC, is:

1. Input characters until you see the STX (2)
2. Input the next character. If it's also STX input another character, because the checksum is enabled and it can sometimes assume the value 2 itself.
3. Input and discard the next three characters after the STX. (You might want the second one since it contains the motion, minus sign, and error bits.)
4. Input the next six characters into your string; that's the displayed weight.
5. If you want to be picky, input and discard six more characters (the tare weight which you probably aren't using)
6. Make sure the next character is CR (13). If it isn't something is wrong, if it is you're golden.

Be aware that those three spaces will NOT always be spaces; the middle one in particular will change when the scale is in motion, under zero, etc.

Of course this might be some other format in which case you can ignore me http://forums.parallax.com/images/smilies/smile.gif

Joms
07-21-2009, 09:29 PM
I guess until now I was under the understanding that the number in brackets behind a variable was its byte wise operator.

VariableName[6]

Would the 6 be the 6th byte in this variable or would it make the string 6 bytes long?

I am still trying to figure out how to treat the string as one variable number.

Joms
07-21-2009, 09:35 PM
Localroger - Your right on in guessing it is a Toledo Scale. It is actually a Jag Extreme. I am splitting the RS422 data that is going to the score-board.

I think now understand what each of the bytes does in the string. I am not using the tare weight, just plain old weight of the truck.

If you look at my program I attached above can you see any errors on how I am inputing the string? The whole project basically has two goals, 1) display the weight using TV_Text, 2) compair the weight and if it is over a certian value, say 5000, activate a relay.

I have the video part working by still trying to compair the data and activate an output. The problem is that I am displaying the whole string yet, even the bytes after the weight, however to get around that I am printing the 'LBS' label over the top to cover it up...

Thanks for all your help so far everyone....This is a big learning step for me...

localroger
07-21-2009, 09:40 PM
OK, that is Toledo format continuous. Here is more info:

Char/function:

1: STX (ASCII 2)
2: Status Word A generally $20 plus some bits indicating scale capacity
3: Status word B important! $20 +
$01 = scale is in net mode
$02 = weight under zero
$04 = over capacity
$08 = scale in motion
$10 = KG mode

4: Status word C varies a bit, bit $08 generally indicates PRINT button is pushed
5-10 = displayed weight
11-16 = tare weight
17 = CR
18 = optional checksum; many modern scales don't output this, but beware it can be $02

Hope this is useful...

localroger
07-21-2009, 09:57 PM
Edit -- I have added accumulation into a string buffer since I just realized you might need the fixed width output for your scoreboard.

Joms, I would use character I/O for this. I don't have time to test this because I need to leave in a few minutes, but off the top of my head I'd advise trying something like this:




var
byte wtbuf[7]

pub readwt | c, n
'wait for STX
repeat while (c := ser.rx) <> 2
'get 3 characters, resetting if another rx
n := 0
repeat while n < 3
if (c := ser.rx) == 2
n := 0
else
n++
'get 6 characters and accumulate them as weight
result := 0
n := 0
repeat 6
c := ser.rx
wtbuf[n++] := c
if c <> $20
'ignore leading spaces
'make sure we're really reading numbers
if c < "0" or c > "9"
abort 'something is wrong
else
'mask this digit to convert ASCII to numeric,
'and shift into result
result := (result * 10) + (c & $F)
'null terminate string result
wtbuf[n] := 0




This will fairly reliably give you the last valid weight reading as the return value for readwt, and collect the 6 fixed width digits of the weight string into wtbuf.

You can add timeout/error checks, etc. One nice thing about continuous output is that you don't have to worry about blocking calls like rx; as long as the scale is connected you will get another character very shortly.

Post Edited (localroger) : 7/21/2009 2:06:59 PM GMT

Mike Green
07-21-2009, 10:11 PM
Based on localroger's description, here's a read routine:


VAR byte statusA, statusB, statusC
' Read and return the displayed weight as an integer. If a timeout occurs, return a negative number.
' Set byte values "statusA", "statusB", and "statusC" to the bytes received after the STX (2).
' Status Word A generally $20 plus some bits indicating scale capacity
' Status word B important! $20 +
' $01 = scale is in net mode
' $02 = weight under zero
' $04 = over capacity
' $08 = scale in motion
' $10 = KG mode
' Status word C varies a bit, bit $08 generally indicates PRINT button is pushed
PRI readScale | c
'1. Input characters until you see the STX (2)
repeat until (c := scale.rxtime(100)) == 2
if c == -1
return -2
'2. Input the next character. If it's also STX input another character, because the checksum is enabled and it can sometimes assume the value 2 itself.
if (statusA := scale.rxtime(100)) == 2
statusA := scale.rxtime(100)
if statusA == -1
return -3
'3. Input and discard the next three characters after the STX. (You might want the second one since it contains the motion, minus sign, and error bits.)
if (statusB := scale.rxtime(100)) == -1
return -4
if (statusC := scale.rxtime(100)) == -1
return -5
'4. Input the next six characters into your string; that's the displayed weight.
repeat 6
case c := scale.rxtime(100)
-1: return -6
" ": result := result * 10
"0".."9":
result := result * 10 + c - "0"
'5. If you want to be picky, input and discard six more characters (the tare weight which you probably aren't using)
repeat 6
if (c := scale.rxtime(100)) == -1
return -7
'6. Make sure the next character is CR (13). If it isn't something is wrong, if it is you're golden.
if (c := rxtime(100)) <> 13
return -1

Mike Green
07-22-2009, 04:51 AM
1) That's fine. You will eventually need two copies of readScale, one using loadoutscale and one using loadinscale as you indicated.

2) Yes, a typo

3) I can't tell what you're trying to do since you didn't include GUI. loadout should be a long variable and "loadout := readScale" reads a value and assigns the weight value to loadout. It also has a side effect of setting statusA, statusB, and statusC.

4) If you want, you could add a test for a negative value for loadout which would indicate a timeout or missing final carriage return character.

Joms
07-22-2009, 05:33 AM
Yes, Actually I was having problems makeing the one work that Mike posted so I am now playing with Rogars. I am getting it to work but I wanted to take it one step farther and add a time-out line incase one scale got disconnected the other would still work.

I have actually tried a few different things and this is where I am at at the moment...

I removed all of the comments that Roger put in and commented out only the lines I added. I did not save it this was but did this only for this post.




pub readwtloadin | c, n, t

t := 0 'Set timeout to zero
repeat while ((c := loadindata.rx) <> 2) and (t < 10) 'Added t<10 timeline here
++t 'Increment T by one
n := 0
repeat while n < 3
if (c := loadindata.rx) == 2
n := 0
else
n++
result := 0
n := 0
repeat 6
c := loadindata.rx
loadin[n++] := c
if c <> $20
if c < "0" or c > "9"
abort
else
result := (result * 10) + (c & $F)
loadin[n] := 0




Do you see why this timeout command wouldn't work?

EDIT - I think I figured it out... I have to use the rxtime instead of rx in addition to the code above...

Post Edited (Joms) : 7/21/2009 10:24:02 PM GMT

localroger
07-22-2009, 06:49 AM
Joms -- assuming this is a simple application, another way to handle it is to start two cogs with COGNEW, one continuously reading the inbound scale and the other continuously reading the outbound. That way if either freezes the other is running in another cog. (This kind of wastes a cog but I get the impression your app isn't exactly running out of them.) Oh, in my code you might want to change the abort to return -1 so you can tell what happened and it won't top out your program; I meant that line as kind of "handle an abort here however you would." Mike did this by returning error codes for the various failure modes.

You can use rxtime for all your .rx calls but you should check for -1 and bail if it happens. I don't know what you're using your relay output for but it's kind of embarrassing when the fill valve stays open because the scale died and your weight output froze... If you use rxtime you don't need to use timeout code yourself; just give it the appropriate number of milliseconds and look for the -1 return value.

It's also really a good practice to grab SWB and check the error and under-zero bits too for the same reason. You can do that by saving it in the repeat while n < 3 loop, right before the else put elseif n == 2 / swb := c, then you can check swb & various maps to make sure everything is OK.
You might also want to look at the source for the rxtime function, it' s a bit cryptic but it's the better way to handle timeouts on the prop; you grab the system cnt variable, and keep subtracting that start value from the progressively later new current cnt. When the difference is greater than your timeout in clocks, you handle the timeout. This has the advantage of working properly at any clock rate if you use clkfreq to set the difference, and due to the magic of modulo 2 integer math it even works when the counts roll over. On an 80 MHz prop this is good to about 50 seconds.

I've used -1 for the bad return code everywhere but since some of these indicate a scale that is working but in error, you might want to follow Mike's example and use different codes so you can tell the operator that the scale is in the wrong mode instead of no data, etc. But remember that negative return values are valid, just not integer values that aren't multiples of 20.



pub readwtloadin | c, n, t, swb

repeat
'bail if timeout, otherwise wait for stx
if (c := loadindat.rxtime(100)) == -1
return -1
elseif c == 2
quit
n := 0
repeat while n < 3
if (c := loadindata.rxtime(100)) == -1
return -1
elseif c == 2
n := 0
elseif n == 1 '0-based, post increment
swb := c
else
n++
if (swb & $15)
'scale is in net or kg mode or overcapacity
return -1
result := 0
n := 0
repeat 6
c := loadindata.rxtime(100)
if c == -1
return -1
loadin[n++] := c
if c <> $20
if c < "0" or c > "9"
return -1
else
result := (result * 10) + (c & $F)
if (swb & 2)
'weight is below zero
-result
loadin[0] := "-"
loadin[n] := 0

Post Edited (localroger) : 7/21/2009 11:02:45 PM GMT

Joms
07-22-2009, 07:00 AM
I have most of it working.... Thought I had it all working but when I debug I only see up to 255, then it starts over. Almost like I am only seeing the data of the first byte and it is cutting the rest off. However, it does look correct on the display using TV_Text.

Does anyone see anything quick that looks like I have the format wrong, or storing a variable wrong?


I can post the full code as an attachement if it will help, but thought it would be easier to take a quick look just pasting it as code...




VAR

byte loadin [7]
byte loadout[7]
byte loadoutwt[7]
byte loadinwt[7]







PUB Main

REPEAT

loadin := readloadinwt
Display.loadin(@loadinwt)

loadout := readloadoutwt
Display.loadout(@loadoutwt)



Debug.dec(loadout)
debug.tx(13)

if loadin > 4000
outa[10]~~
else
outa[10]~

if loadout > 4000
outa[9]~~
else
outa[9]~


pub readloadinwt | c, n, t

t := 0
repeat while (c := loadindata.rxtime(100)) <> 2
++t
if t > 25
Display.loadin(string(" --"))
return
n := 0
repeat while n < 3
if (c := loadindata.rxtime(100)) == 2
n := 0
else
n++
result := 0
n := 0
repeat 6
c := loadindata.rxtime(100)
loadinwt[n++] := c
if c <> $20
if c < "0" or c > "9"
abort
else
result := (result * 10) + (c & $F)
loadinwt[n] := 0





Thanks for all your help everyone on this project.... I could never have done it alone... We are almost there...

localroger
07-22-2009, 07:02 AM
Your loadinwt and loadoutwt should be declared as longs, they are numeric and they go beyond 16 bits. edit -- and not arrays, e.g. long loadinwt / long loadoutwt. edit2 -- and you use loadoutwt / loadinwt for the math comparisons.

Post Edited (localroger) : 7/21/2009 11:07:28 PM GMT

Joms
07-22-2009, 07:13 AM
OK... I was able to fix the first problem, but only with the following combination...




long loadin [7]
long loadout[7]
byte loadoutwt[7]
byte loadinwt[7]




I tried different combinations, making the loadoutwt a long, and it would either cause the debug or the TV_Text to quit working... But it is going now, so that part is good, I am just going to play around a little more with the combinations to try and learn why it is that way.

EDIT -

Also, I can not get the math part to work either way. I tried the loadout, loadoutwt, and @loadoutwt. I do know my hardware is working because I changed the pin on my heartbeat from 8 (LED) to 9 (relay), and the relay cycles...

Does the number have to be in hex or something other then a decimal? I was using a decimal because that is how I am debugging it.




Debug.dec(loadout)
debug.tx(13)

if loadinwt > 4000
outa[10] := 1
else
outa[10] := 0

if loadoutwt > 4000
outa[9] := 1
else
outa[9] := 0




EDIT 2 -
Something simple here is wrong that I don't understand. I even moved my 'outa[9] := 1' to just under the debug statement which should activate the relay, but it does not. I am declaring them outputs earlier in the program... 'dira[9..10] := 1'

Post Edited (Joms) : 7/21/2009 11:29:38 PM GMT

localroger
07-22-2009, 07:31 AM
Better... as I wrote mine, you should be assigning the results of the call to loadinwt and loadoutwt and lose the [7]'s after them because they aren't arrays. You then use loadinwt and loadouwt for your comparisons with 4000. You might also want to explicitly test for loadinwt and loadoutwt == -1 and set your outputs appropriately (though if this is as I suspect a presence detector, seeing error as no truck is fine behavior). Though here's a sneaky trick you might want to use:

elseif loadinwt == -1
'pretend the next line is indented
outa[10] := (cnt & $4000) == 0

What this will do is blink your output if the weight is an error condition; it masks the ever changing cnt against a bit which should be changing every second or so at 80 MHz.

I don't know what your display object is but that usage doesn't look right. As I've written it you can use any of the usual debug, vga, tv drivers to output the fixed width weights like this:

object.str(@loadin)
object.out(13) 'carriage return

localroger
07-22-2009, 07:33 AM
Joms, post the whole spin file and I'll check it all over. I need the obj declarations too because I don't know what display is.

Joms
07-22-2009, 07:36 AM
I think this is most of it. The other stuff should already be in the library. Thanks for your help so much... I am just working on the code also trying a few different things...

Also, sorry for the lack of comments, I am just putting those back and and adding extra ones...

Edit -

Yes, it is a make-shift presence detector. I need the data to display the entire time on the monitor, but if the scale goes over say 4000 (just something random that is less then a truck but more then a person or lawn-mower), it will activate the relay. There is a seperate relay for each scale. Is there a better way to do that then?

Post Edited (Joms) : 7/21/2009 11:45:35 PM GMT

localroger
07-22-2009, 08:02 AM
OK, I thinks I gots it. There's been some confusion so I've standardized like this:
readloadin / readloadout = the read functions, they return -1 for error or a weight as result and incidentally set the fixed-length string outputs.
loadin / loadout = the string outputs, these are byte arrays of dimension 7 for 6 digits + null terminator.
loadinwt / loadoutwt = the numeric weight values, these are longs and you use them to do math.


VAR
byte loadin [7]
byte loadout[7]
long loadoutwt
long loadinwt


PUB Main
REPEAT

loadinwt := readloadin
Display.loadin(@loadin)
loadoutwt := readloadout
Display.loadout(@loadout)

Debug.dec(loadoutwt)
debug.tx(13)

if loadinwt > 4000
outa[10] := 1
else
outa[10] := 0

if loadoutwt > 200
outa[9] := 1
else
outa[9] := 0

pub readloadin | c, n, t, swb
repeat
'bail if timeout, otherwise wait for stx
if (c := loadindata.rxtime(100)) == -1
return -1
elseif c == 2
quit
n := 0
repeat while n < 3
if (c := loadindata.rxtime(100)) == -1
return -1
elseif c == 2
n := 0
elseif n == 1 '0-based, post increment
swb := c
else
n++
if (swb & $15)
'scale is in net or kg mode or overcapacity
return -1
result := 0
n := 0
repeat 6
c := loadindata.rxtime(100)
if c == -1
return -1
loadin[n++] := c
if c <> $20
if c < "0" or c > "9"
return -1
else
result := (result * 10) + (c & $F)
if (swb & 2)
'weight is below zero
-result
loadin[0] := "-"
loadin[n] := 0

pub readloadout | c, n, t, swb
repeat
'bail if timeout, otherwise wait for stx
if (c := loadoutdata.rxtime(100)) == -1
return -1
elseif c == 2
quit
n := 0
repeat while n < 3
if (c := loadoutdata.rxtime(100)) == -1
return -1
elseif c == 2
n := 0
elseif n == 1 '0-based, post increment
swb := c
else
n++
if (swb & $15)
'scale is in net or kg mode or overcapacity
return -1
result := 0
n := 0
repeat 6
c := loadoutdata.rxtime(100)
if c == -1
return -1
loadout[n++] := c
if c <> $20
if c < "0" or c > "9"
return -1
else
result := (result * 10) + (c & $F)
if (swb & 2)
'weight is below zero
-result
loadout[0] := "-"
loadout[n] := 0

Joms
07-22-2009, 08:04 AM
I have been working on commenting out the lines... I have attached the new revised file... It really helps me to re-write the comments because then I can figure out exactly what each line does....

Also, I was re-reading through the posts and like your idea of using multiple cogs. Actually what I was playing with earlier is making a sperate spin file for the Jag Extream controller... Bascially any time that I need to input the weight of a jag scale I could just use the jagextream.spin file. In my main program I would just have to declare the RX pin and like the file return the weight.

Is this worth the programming? Would it actually save time and coding or take more? What do you think of the idea? I could see using this in the future again...

Joms
07-22-2009, 08:10 AM
Hmmm... I am just going through the code....

It will debug a '-1' and will not display anything on the screen....

Joms
07-22-2009, 10:20 AM
WE ARE THERE!!!! I think we have everything working as it should now... I am just going back through the program to try and get a better understanding of exactly how it works.

I would like to thank everyone for your help!

Mike and Roger, I couldn't have done it without you. Thank you so much for not only helping me with the code but taking the time to explain it so I could learn more about the prop and how to program it. I have learned a lot from this project. I can not say enough about how great it was to have you help on this project and other projects on the forums. THANKS!

localroger
07-22-2009, 10:58 AM
Good deal Joms. I think the Prop was designed not just to do great things, but to teach as it does. This is a wonderful example of how it was meant to be used.