# Oshonsoft programs with INTERRUPTS and PARSE

#### camerart

Joined Feb 25, 2013
2,049
Yep, typo. Sorry.

That's because if you are not padding the value to 3 digits e.g. 099, the length of the string will vary with the number of degrees and the length test will fail. Even without the length test, the parsing of the digits will fail since both your original MidStr and my replacement expect a fixed number of digits in a known place in the string. If QEIDEG works that way, we have to change how the received sentence is parsed.
Correct. We still need to add a timeout for missing messages to avoid it hanging up. It's on the list.
I expect that is because the HC-12 is is half-duplex. You are sending back the result of the previous sentence to the PC when the REMOTE message is sent. It won't get through until the transmission to the PC is over.
Hi JT,
I just tried an old program with MIDSTR and it show single and double digits. I'm not sure what the difference is between MIDSTR and your method is, but I'll investigate a little to see if I pad out e,g, 099.

Yes, the REMOTE messages are sent 5/SEC, so don't wait till there's a gap. I suppose that when it comes to programming the REMOTE it could be, that it doesn't TX till RX is finished.
Thanks.
C.

#### JohnInTX

Joined Jun 26, 2012
4,113
Post that code?

#### camerart

Joined Feb 25, 2013
2,049
Hi J
Post that code?
Hi JT,
Here's the MAIN program including MIDSTR:
C.

#### Attachments

• 26.3 KB Views: 4

#### JohnInTX

Joined Jun 26, 2012
4,113
Yeah... the parse routine in that one adds one character to strbuf and then re-parses everything once for as many characters as you received. That's.not.the.way.to.do.it. As written, I think regardless of how many degree digits you get in the QEIDEG string, if it's terminated with ,W, you'll append ,W to your degree readout. I don't think that's what you want. The reason MidStr works at all is that you build a null terminated string for each character in the receive buf. MidStr will stop there.
It's moot though since that's not the way things are heading.

In a nutshell, if you want to use the length from $-W to help qualify a good string, they all have to be a known length and you can't trim off leading zeros. If that's the way QEIDEG works and you don't want to change it, we can eliminate the length check for that one and tune up the digit extraction to handle the variable number of digits. Which do you prefer? Thread Starter #### camerart Joined Feb 25, 2013 2,049 Yeah... the parse routine in that one adds one character to strbuf and then re-parses everything once for as many characters as you received. That's.not.the.way.to.do.it. As written, I think regardless of how many degree digits you get in the QEIDEG string, if it's terminated with ,W, you'll append ,W to your degree readout. I don't think that's what you want. The reason MidStr works at all is that you build a null terminated string for each character in the receive buf. MidStr will stop there. It's moot though since that's not the way things are heading. In a nutshell, if you want to use the length from$-W to help qualify a good string, they all have to be a known length and you can't trim off leading zeros. If that's the way QEIDEG works and you don't want to change it, we can eliminate the length check for that one and tune up the digit extraction to handle the variable number of digits.
Which do you prefer?
Hi JT,
If I understand correctly, I prefer the second choice.

(I think I have this correct, but please correct me)
The second PIC on the PCB 184431 outputs a QEI HIGH BYTE and a LOW BYTE, which is a number between 0 and 255 each, which added together makes a WORD. This is set to zero after 359. So the outut is 0 to 359, which is the TX.

The program can be re-programmed to modify this results into something better for the 18LF4620 RX if this would be helpful. e,g, Add zeros where there isn't a digit?
C.

#### JohnInTX

Joined Jun 26, 2012
4,113
If I understand correctly, I prefer the second choice.
I think that's the wise choice.
(I think I have this correct, but please correct me)
The second PIC on the PCB 184431 outputs a QEI HIGH BYTE and a LOW BYTE, which is a number between 0 and 255 each, which added together makes a WORD. This is set to zero after 359. So the outut is 0 to 359, which is the TX.
According to your old parse code and the examples in your 'sentences.txt' test file, it expects that the QEI degrees are expressed as 3 ASCII characters "000' to '359'. Parse builds an ASCII string into qeideg, a string variable. Saying it sends a high byte and low byte implies some other coding that isn't ASCII. That would be problematic. If you sent 124 degrees as 01h 24h you would inadvertently send a '$' in the middle of your message which resets the receive code (24h is ASCII '$'. So recheck that code and see what it actually sends.
The program can be re-programmed to modify this results into something better for the 18LF4620 RX if this would be helpful. e,g, Add zeros where there isn't a digit?
That's what I would do. 3 ASCII characters. 000 degrees is 30h 30h 30h. 359 degrees is 33h 35h 39h and so on. It keeps the QEIDEG message the same length always and the coding is consistent with everything else. That's important. Plus, if you have a compass error, you can send '---' or '???' '999' or something like that.

#### camerart

Joined Feb 25, 2013
2,049
I think that's the wise choice.
According to your old parse code and the examples in your 'sentences.txt' test file, it expects that the QEI degrees are expressed as 3 ASCII characters "000' to '359'. Parse builds an ASCII string into qeideg, a string variable. Saying it sends a high byte and low byte implies some other coding that isn't ASCII. That would be problematic. If you sent 124 degrees as 01h 24h you would inadvertently send a '$' in the middle of your message which resets the receive code (24h is ASCII '$'. So recheck that code and see what it actually sends.
That's what I would do. 3 ASCII characters. 000 degrees is 30h 30h 30h. 359 degrees is 33h 35h 39h and so on. It keeps the QEIDEG message the same length always and the coding is consistent with everything else. That's important. Plus, if you have a compass error, you can send '---' or '???' '999' or something like that.
Hi JT,
Ok, choice to then.

The 4431 CODE sends as you say 3x digits, I only mentioned the 2x BYTEs in case it was helpful.
Here is the CODE section:
Code:
qeideg.LB = CAP2BUFL
qeideg.HB = CAP2BUFH
While SSPBUF = 0
Wend
If SSPBUF = qeideglb Then
SSPBUF = qeideg.LB
Else
If SSPBUF = qeideghb Then
SSPBUF = qeideg.HB
Endif
Endif

Hserout "$QEIDEG", ",", #qeideg, ",", "W", "X", "X", CrLf ( The 2x Xs are from my CODE, to correct it, I'll remove them.) I'll see if I can re-program the 4431 CODE to suit. Watch this space C. Last edited: #### JohnInTX Joined Jun 26, 2012 4,113 Cool! Why does it end with X,X? We ignore everything after the first W Thread Starter #### camerart Joined Feb 25, 2013 2,049 Cool! Why does it end with X,X? We ignore everything after the first W Hi JT, See #308 C #### JohnInTX Joined Jun 26, 2012 4,113 #### JohnInTX Joined Jun 26, 2012 4,113 Here's an idea to get leading zeros in a fixed length field of degrees. It examines the value and adds as many zeros as necessary to pad the character output. Of note is the additional test that types '---' for any out of range value. You could use that to inform the receiving unit that the compass is not working. Just a thought. I wrote it as a procedure but you could use the general idea to write it in conventional BASIC if you're more comfortable with that. Code: 'ShowDegrees: Types out 3 digits of passed value in a 3 character field with leading zeros Proc ShowDegrees(value As Word) If value < 100 Then 'needs at least one leading zero Hserout "0" Endif If value < 10 Then 'needs another leading zero Hserout "0" Endif If value < 360 Then Hserout #value, CrLf 'will type 1,2,or 3 remaining digits of 0 <= value <= 359 degrees Else Hserout "---", CrLf 'will type out --- as an error message Endif Return End Proc Here's what I used to test it: Code: Dim deg As Word TestShowDegrees: deg = 0 Call ShowDegrees(deg) deg = 9 Call ShowDegrees(deg) deg = 10 Call ShowDegrees(deg) deg = 99 Call ShowDegrees(deg) deg = 100 Call ShowDegrees(deg) deg = 359 Call ShowDegrees(deg) deg = 400 Call ShowDegrees(deg) Return Calling TestShowDegrees, you get: Cheers! #### JohnInTX Joined Jun 26, 2012 4,113 Actually the simplest thing to do is to integrate the leading zero code into your stuff in #307 and let it go at that for now: Code: qeideg.LB = CAP2BUFL qeideg.HB = CAP2BUFH While SSPBUF = 0 Wend If SSPBUF = qeideglb Then SSPBUF = qeideg.LB Else If SSPBUF = qeideghb Then SSPBUF = qeideg.HB Endif Endif Hserout "$QEIDEG", ","
If qeideg < 100 Then  'needs at least one leading zero
Hserout "0"
Endif
If qeideg < 10 Then  'needs another leading zero
Hserout "0"
Endif
Hserout #qeideg, ",", "W",Crlf   'will type 1,2,or 3 remaining digits of 0 <= value <= 359 degrees
For qeideg = 9, 24 and 359 it sends:
$QEIDEG,009,W(Crlf)$QEIDEG,024,W(Crlf)
$QEIDEG,359,W(Crlf) Ship it! Last edited: Thread Starter #### camerart Joined Feb 25, 2013 2,049 Last edited: Thread Starter #### camerart Joined Feb 25, 2013 2,049 Here's an idea to get leading zeros in a fixed length field of degrees. It examines the value and adds as many zeros as necessary to pad the character output. Of note is the additional test that types '---' for any out of range value. You could use that to inform the receiving unit that the compass is not working. Just a thought. I wrote it as a procedure but you could use the general idea to write it in conventional BASIC if you're more comfortable with that. Code: 'ShowDegrees: Types out 3 digits of passed value in a 3 character field with leading zeros Proc ShowDegrees(value As Word) If value < 100 Then 'needs at least one leading zero Hserout "0" Endif If value < 10 Then 'needs another leading zero Hserout "0" Endif If value < 360 Then Hserout #value, CrLf 'will type 1,2,or 3 remaining digits of 0 <= value <= 359 degrees Else Hserout "---", CrLf 'will type out --- as an error message Endif Return End Proc Here's what I used to test it: Code: Dim deg As Word TestShowDegrees: deg = 0 Call ShowDegrees(deg) deg = 9 Call ShowDegrees(deg) deg = 10 Call ShowDegrees(deg) deg = 99 Call ShowDegrees(deg) deg = 100 Call ShowDegrees(deg) deg = 359 Call ShowDegrees(deg) deg = 400 Call ShowDegrees(deg) Return Calling TestShowDegrees, you get: View attachment 211753 Cheers! Hi JT, I didn't receive your instructional messages Anyway, I've been all over the place with BYTES, WORDS, STRINGS and ASCIIS, then my first idea started working. Here it is: I tried it in SIM which looks ok, but LIVE no result. It could be something simple. I'll re-check it, but perhaps you can also check it to see if it looks ok, please? C. #### Attachments • 3.3 KB Views: 3 • 59 KB Views: 3 #### JohnInTX Joined Jun 26, 2012 4,113 There’s no ‘Return’ instruction at the end of ‘get_valq’ Thread Starter #### camerart Joined Feb 25, 2013 2,049 There’s no ‘Return’ instruction at the end of ‘get_valq’ Hi JT, It's on the program, but I missed it when copying. C Thread Starter #### camerart Joined Feb 25, 2013 2,049 Hi JT, All 3x$Sentences are working.
It's missing some, but generally, most of the messages are repetetive or change little, e,g, Battery voltage from the REMOTE. I try more tests, as it is, and post the results. I suppose the GPS is the main one as the REMOTE may be flying at a bit of speed. This of course is a while from now though.

Everything may change a bit once the INTERRUPT CODE has been added into the MAIN program, as things will slow down.
Cheers, C.

#### JohnInTX

Joined Jun 26, 2012
4,113
Everything may change a bit once the INTERRUPT CODE has been added into the MAIN program, as things will slow down.
So it's working in the stripped-down code we've been playing with?
How did you get the degrees code working in the live one?

Before integrating the interrupt/parse code back into the main program, I would recommend we visit the task of adding guard timer(s) to the receive code to keep it from locking up when the radio is out of range or the GPS acts up. That seems to me to be a show-stopper. It's not hard to do (in fact, I've written it already) but it's your call.

Whatever you decide, you should not re-integrate that module until you are satisfied completely with it. It will be much harder to add or fix things when dealing with the full main program.

#### camerart

Joined Feb 25, 2013
2,049
So it's working in the stripped-down code we've been playing with?
How did you get the degrees code working in the live one?

Before integrating the interrupt/parse code back into the main program, I would recommend we visit the task of adding guard timer(s) to the receive code to keep it from locking up when the radio is out of range or the GPS acts up. That seems to me to be a show-stopper. It's not hard to do (in fact, I've written it already) but it's your call.

Whatever you decide, you should not re-integrate that module until you are satisfied completely with it. It will be much harder to add or fix things when dealing with the full main program.
Hi JT,
Sounds a good idea, send me the CODE you've written and I'll test it, please.
C.

#### JohnInTX

Joined Jun 26, 2012
4,113
Here you go.
Note that at the top you see:
'<><><><><><><><>< TIMER SETUP: <><><><><><><><><><>
'Const PR2set = 250 'use this for the live board: gets 20ms per interrupt
Const PR2set = 2 'use this for fast timing to speed up simulator
The active setting speeds up the timers for use in the sim. Bigger number is slower timers.
Comment that out and uncomment the line above to set PR2 to 250 for the live version.

This code uses TIMER2 to generate an interrupt every 20ms. It drives a set of timers from that interrupt.
Each timer is one byte that counts from whatever value you set (0-255) down to 0 then stops at 0. Each timer is used as a guard timer on various info.

To know if a timer has timed out, just read it and test for 0 as you see in main_loop.

There are 2 periods in use:
20ms/count for the UART receive guard timer. This timer is set and begins to run whenever setUARTchannel is called to select a new channel. The default time is 5secs.
Main waiting for new sentence OR timeout:
    waitBuf:
If buf_done = 0 Then
'Other processing..
If ms20_RXguard = 0 Then
Goto sentence_done
Endif
Goto waitBuf
Endif

'Sentence is received, proceed to parsing it
'Since parsing SHARES some variables with the IRQ service, you can't begin
'looking for the next sentence until parsing is complete.
Gosub parse  'parse the received sentence

'Process any new data from parseing (while new sentence is being received)
If prsed_GNRMC = 1 Then
Hserout "T=", strtim, " N=", strlat, " W=", strlong, CrLf  'process new data
prsed_GNRMC = 0  'and clear flag to indicate that new data has been "processed"
Endif

If prsed_QEIDEG = 1 Then
Hserout "QEIDEG=  ", qeideg, CrLf  'QEIDEG=BASE 4431 Opto encoder
prsed_QEIDEG = 0
Endif

If prsed_REMOTE = 1 Then
Hserout "REMVOLT=", strremvolt, " REMALT=", strremalt, " REMDIST=", strremdist, CrLf
prsed_REMOTE = 0
Endif

If prsed_GOOD = 0 Then  'parse tried but string was unknown, corrupt or wrong length
Hserout "Parse fail Chan: ", #datasw, CrLf
Endif

'-------------- SENTENCE PROCESSING COMPLETE  ---------------
'Done receiving, parsing and processing ONE sentence.
sentence_done:
Gosub next_channel  'select next channel
Gosub setUARTchannel  'start UART on channel(datasw), begins looking for sentence

Goto main_loop
Note that in main_loop, it now polls the timer along with buf_done. Most of the time, buf_done will be true (meaning it got a sentence from \$-W) before the timer runs to 0 and it will fall through to calling parse. If the timer DOES run to 0 before buf_full it means that it did not get a sentence within 5 seconds and it jumps to select the next channel then loops back to see what's happening on that one. In this way, a dead GPS or radio out of range won't lock up the system.

I also added a separate timer for each of the sentence types on the assumption that even if the system did not lock up waiting for a sentence, it would be handy to know which sentence(s) were missing i.e. if it's been too long since the data would be updated. Example, suppose lat and long haven't been updated in awhile, is it at rest or is the GPS dead? Nice to know. These timers are maintained by parse but the code doesn't check them for 0. You can add that as necessary if you want.

The timer names, periods, and default values are shown in the code as:
Timer names and settings:
'receive guard timer
Dim ms20_RXguard As Byte  'native 20ms RX guard timer derived from TMR2 irq
Const RXguardSet = 250  '20ms * 250 = 5 secs to wait for any RX sentence

'Guard timers for each sentence type and their set times
Dim ms100_GNRMCguard As Byte  'GNRMC message guard timer * 100ms
Const GNRMCguardSet = 100  '100ms * 100 = 10 secs to get a good GNRMC sentence

Dim ms100_REMOTEguard As Byte  'REMOTE sentence guard timer *100ms
Const REMOTEguardSet = 100  '100ms * 100 = 10 secs to get a good REMOTE sentence

Dim ms100_QEIDEGguard As Byte  'QEI guard timer *100ms
Const QEIDEGguardSet = 100  '100ms * 100 = 10 secs to get a good QEIDEG sentence
Setting a timer, in this case the RX sentence guard timer, looks like this:
Setting the RX timer in chan_set::
chan_set:
Gosub IRQinitBuf  'RCIE is off, init buffer, index, flags
RCSTA.CREN = 1
RXIRQchar = RCREG
RXIRQchar = RCREG
PIE1.RCIE = 1
ms20_RXguard = RXguardSet 'set RX sentence timer
Return
That's all there is to it.

When you load this in the sim, use the 8xLED window to monitor the Yel and Red LED channel indicators without sending anything. You'll see them changing as ms20_RXguard runs down to 0 and main_loop exits and changes the channel.
In the 'Watch" window, you can see the values of the timers. You'll see the ms20_RXguard running round and round with the channel changing when it goes to 0 and gets reloaded.

Sentence guard timers:
When you send a good sentence and it's parsed, you'll see its timer get set and begin to run down. If you don't send anything else, it will run to 0 indicating it's been a long time since it received that sentence and the data is getting stale. Look at these as juggling to keep 3 balls in the air. As long as you are tossing the balls (receiving and processing valid sentences) they'll stay in the air (timer never runs to 0). Stop juggling and the balls hit the floor (timer runs to 0). Right now, we don't do anything with the timeout indication but if your code is interested in whether your data is recent, read the sentence's timer, if it's non-zero, the data is recent. If 0, stale.

The lesson here is what you've already found out - never wait endlessly on something from the horrible outside world to arrive.. always have some sort of mechanism to keep an eye on things, allow the code to keep moving and give you a chance to take action if something goes away unexpectedly.

Final note: the times were picked out of the air and are likely longer than necessary. They can be adjusted by changing the Const... values near the top of the file. I'm not sure what your sentence rate is.

This is new stuff but worth the study.
Good luck!