Read a specific string from data on a line

Thread Starter

Longymod

Joined Jan 29, 2018
13
My terminology may be incorrect so I hope I explain this enough for someone to help.

I would like to monitor the Rx of a RS232 data line which will be having data sent on it once a second. I would like to to put a PIC in parallel with this that will react when a specific message is received. This will then go on to turn on or off another pin depending on message received.

I am happy to work this all out I jus need the first pointer in the right direction towards what material to start reading up on. Or any advice on a more simpler circuit.

Thank you in adance
 

Thread Starter

Longymod

Joined Jan 29, 2018
13
hi Long,
Do you have a sample of the RX RS232 text that you could post.?
E
We have not currently decided on the message yet for it to read which may make this easier.

What we currently have is a radio modem at 2 locations. We have a GPS antennas position being sent from modem 1 to modem 2 where we then read the string into our software and we then have the reverse in the oppposio direction.

At location 1 there is a piece of kit that is only required to be powered up once or twice a day for a short period. We would like to be able to control it from location 2. We would like to send a string of our choice using an RS232 terminal such as 'termite' to be read into the PIC which connects to a switching circuit.

I hope this helps and makes sense
 

ericgibbs

Joined Jan 29, 2010
15,691
hi,
The method I use for RS232 GPS data strings, is to read and save the incoming data into a buffer.
The different parameters within the data string are separated using commas ','.
To select a parameter read the buffer from its comma location within the string.
You can then compare the parameter with a list of stored parameters to perform the required action.
Always consider a default action in the event of a failed reception of the main data string.

E
 

Thread Starter

Longymod

Joined Jan 29, 2018
13
hi,
The method I use for RS232 GPS data strings, is to read and save the incoming data into a buffer.
The different parameters within the data string are separated using commas ','.
To select a parameter read the buffer from its comma location within the string.
You can then compare the parameter with a list of stored parameters to perform the required action.
Always consider a default action in the event of a failed reception of the main data string.

E
That makes sense, I have a starting point now.

Thanks Eric for the quick reply I appreciate it
 

ericgibbs

Joined Jan 29, 2010
15,691
hi,
Another point would be to give your data string an ID Header, give you option of addressing more than one remote.
I use 'A' or 'B' , just a single leading sync character, end the string with CrLf codes.
E
 

MrChips

Joined Oct 2, 2009
26,480
The process of interpreting the information in a string is called parsing.
If you have control on the formatting of this string this will make parsing easier. The rules of the format is called the protocol.

The simplest protocol defines two unique characters, one for the start of the string and the second for the end of the string. Both of these characters would normally never appear in the message of the string.

Common start characters are $ (decimal 36) and * (decimal 42).

Common stop character is CR (decimal 13).

It is good advice to include some form of error checking (called checksum) at the end of the message preceding the STOP character.

If the character set employed is restricted to printable characters, this would assist in the debugging process since you can display the message using a common terminal emulator program.

I can provide you with an example of a simple protocol.
 

Thread Starter

Longymod

Joined Jan 29, 2018
13
The process of interpreting the information in a string is called parsing.
If you have control on the formatting of this string this will make parsing easier. The rules of the format is called the protocol.

The simplest protocol defines two unique characters, one for the start of the string and the second for the end of the string. Both of these characters would normally never appear in the message of the string.

Common start characters are $ (decimal 36) and * (decimal 42).

Common stop character is CR (decimal 13).

It is good advice to include some form of error checking (called checksum) at the end of the message preceding the STOP character.

If the character set employed is restricted to printable characters, this would assist in the debugging process since you can display the message using a common terminal emulator program.

I can provide you with an example of a simple protocol.
Thanks MrChips. I will take both of your advice and do some studyng and playing around. I will skip on the example on this occasion so it forces me to research a bit more.
 

John P

Joined Oct 14, 2008
1,978
You're saying "RS232" when you probably mean "serial port". RS-232 (note hyphen) has signal levels of +/- 12 volts, which you do not want to apply to microcontroller pins. But most likely it's actually a logic level signal. If it's at the 12V level, you need to convert it, which is very easy.

One thing to consider is what's happening on the transmission line (the one going from point 2 to point 1) under "normal" conditions. You say that you need to send a command a few times a day. But what is that line used for generally? If the signal on it is produced by some kind of equipment that you can't easily control, then it could be a problem, because your command might collide with that equipment's output, producing a garbled message. But if the signal is sent by a computer and you're the one who programs that computer, then it's trivial. Just insert your message among the other messages going over the same line. And make sure that the message can never be confused with anything that the computer might send for another purpose. By the way, a cyclic redundancy check (CRC) is more reliable than a checksum, but it's harder to compute.
 

LesJones

Joined Jan 8, 2017
3,745
This sounds similar to some units I have made to remotely monitor various readings such as voltage, current, temperature and humidity. The communication link uses HC12 (433 Mhz travceiver modules.) The sensor modules are based on a PIC12F1840. These sensor modules listen on the channel. They respond to a very simple ID string which consists of a "#" character followed by an upper case letter. The first thing it does when it see's a received character is to test to see if it is a "#" If it is then it waits for a limited amount of time for the next character. If that character is the address of that sensor then it transmits the reading as a text string. (This very simple coding only allows for 26 station IDs.)
Here is the code for the remote voltage monitor (Which is the simplest as it just reads the ADC in the pic.)

Code:
;   This is for reading voltge from ADC input and transmitting as ASCII string of characters
;    Started 19/02/2017
;   Modified to disable USART receive after recognising station ID (18/03/17)
;******************************************************************************************

;

;******************************************************************************************
;
;  OSC  : Internal OSC 4 MHz
;
;******************************************************************************************
  LIST  P=PIC12F1840,ST=OFF,R=DEC
  INCLUDE  "P12F1840.inc"

  __CONFIG _CONFIG1,  _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF &  _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF


  __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_HI & _LVP_OFF

;*******************************************************************************
; Constants
;*******************************************************************************

Station_ID   equ   'D'   ;Station identification character for this station  


RAM_START  equ  0x20


; The periods are timed with timer1 which is set to run at the internal clock
; rate (4 Mhz) of Fosc/4 or 1.0MHz which is equal to a 1 uS period.




;DELAY_6MS   equ   D'2993'    ;Value to give delay of 6 mS
RX_Timer   equ   D'200'

;*******************************************************************************
; Pin Assignments
;*******************************************************************************
;
;   PIC signals
;
   #DEFINE     TX_Data     PORTA, 0     ;
   #DEFINE     RX_Data     PORTA, 1     ;
;
   #DEFINE     AN2     PORTA, 2     ; Analogue input
;   #DEFINE     Button     PORTA, 3     ; Push button to trigger  read (Not used)


; I/O pin use
;   RA0   TX      (Pin 7)  (To pin 4 on programmer connection)
;   RA1   RX      (Pin 6)  (To pin 5 on programmer connection)
;   RA2   Analogue input     (Pin 5)
;   RA3   Trigger button     (Pin 4)   NOTE THIS PIN CAN ONLY BE USED AS AN INPUT.  
;            (This is VPP for programming This could be connected to pin 3 or 5 of the header to program the chip on header.)
;   RA4 (AN3)   Not used     (Pin 3)
;   RA5       (Pin 2)




;  Define GENERAL PURPOSE RAM AREA (80 bytes maximum)

;*******************************************************************************
; File Register Variables
;*******************************************************************************
  cblock  RAM_START

param1:   1  ; parameter 1  (Used in delay cycles routine)
param2:   1  ; parameter 2  (Used in delay cycles routine)
Temp_1:   1     ;Used in 2 second delay

tmpData:   1     ; Used in output_hexbyte routine

; delay counters
Del_Count:  1
  
ADC_Temp_L:   1
ADC_Temp_H:   1


; byte counter
  bytcnt:  1

ESEVN:   1     ; 10 000,000 (digit 8)
ESIX:   1     ; 1000,000s (digit 7 )
EFIVE:   1     ; 100,000s digit 6 store
EFOUR:   1     ; 10,000s digit 5 store
ETHREE:   1     ; 1000s digit 4 store
ETWO:   1     ; 100s digit 3 store
EONE:   1     ; 10s digit 2 store
EZERO:   1     ; 1's digit 1 store



COUNT:   1

BCD1:   1     ; overrange
BCD2:   1     ; MS decimal value
BCD3:   1     ;
BCD4:   1     ; decimal value
BCD5:   1     ; ls decimal value
  
BIN1:   1     ; LS binary value
BIN2:   1     ;
BIN3:   1     ;
BIN4:   1     ; MS binary value

TEMP:   1

RX_Count:   1


  endc
;*******************************************************************************
; Common RAM (0x70 to 0x7F)
;*******************************************************************************

TX_temp     EQU 0x70     ;Temporary storage for character to be transmitted.
RX_Temp     EQU 0x71     ;Temporary storage for received character.
BS_Temp     EQU 0x72     ;Temporary storage for "W" during bank select
;*******************************************************************************
;  Define Macro  Takes 2 uS
;*******************************************************************************
SELBANK MACRO  #BANK_NO
  MOVLB  #BANK_NO   ;1 cycle - 1 uS  
  ENDM


; *****************************

  ORG  0h
  GOTO  START
  ORG  4h
  GOTO  START
START
; *****************************
;  Initialized Segment
; *****************************
; Initialise OSC (4MHz, IntOSC)
  SELBANK 1  ; SET BANK1
  MOVLW  B'11101010'  
  MOVWF  OSCCON

   BCF   INTCON,GIE

; Initialise I/O port
  SELBANK 1  ; SET BANK1
  MOVLW  B'00001110'     ; Bits 0, 4, 5 output Bit 1, 2,  3 input.
  MOVWF  TRISA

;Set pullup on PORTA,3

   BCF   OPTION_REG,NOT_WPUEN

     SELBANK 4  ; SET BANK 4
   MOVLW   B'00001000'
   MOVWF   WPUA

     SELBANK 3  ; SET BANK 3
   MOVLW   B'00000100'
   MOVWF   ANSELA     ; Set bit 2 as analogue, set the rest to digital

     SELBANK 2  ; SET BANK2

; Initialized Comparitor.
     MOVLW  B'00000000'     ; Comparator disabled.
   MOVWF   CM1CON0       ;In bank 2
     SELBANK 0  ; SET BANK0

   CLRF   PORTA     ;Set all outputs low



; Initialized EUSART
     SELBANK 3  ; SET BANK 3
   MOVLW   B'00000000'
   MOVWF   BAUDCON

   BSF   TXSTA,BRGH   ; Set baud rate high bit
     BSF  TXSTA,TXEN  ;enable transmission 

   MOVLW   0x19     ; Decimal 25 for 9600 baud rate
   MOVWF   SPBRGL
   MOVLW   0x00
   MOVWF   SPBRGH

   BSF  RCSTA,SPEN  ;enable serial port
   BSF  RCSTA,CREN  ;enable receive

     SELBANK 0  ; SET BANK0

; Initialized Timer 1
     SELBANK 0  ; SET BANK0

   MOVLW   B'00010100'   ;Clock source FOSC, 1:2 prescale, Dedicated Timer1 oscillator circuit disabled,
  ;Do not synchronize external clock input, Timer off

   MOVWF   T1CON     ;With 4 Mhz clock timer will increment every 500 nS

   MOVLW   0x00     ;All bits clear (Gate control not used.)
   MOVWF   T1GCON

; Initialized ADC

     SELBANK 1  ; SET BANK1
   MOVLW   0x09     ; Channel 2, ADON (Bit 0)
   MOVWF   ADCON0

   MOVLW   B'11010011'   ; Bit 7 Result right justified. ADCS 101 /16 (Bits 6 & 4 set ).  ADPREF bits 0 & 1 set (FVR module.)
   MOVWF   ADCON1

     SELBANK 2  ; SET BANK2
   MOVLW   B'10000011'   ;Bit 7 enable. Bits 0,1 4.096V ref
   MOVWF   FVRCON

     SELBANK 0  ; SET BANK0

; *****************************
;  Program main
; *****************************
; Main loop

;   CALL Delay_2_Sec

MAIN:

Wait_Rx_Chr:       ;Wait for a received character

   SELBANK 3
   BSF  RCSTA,CREN  ;enable receive
   SELBANK 0

   bcf   PORTA,5     ;   TEST switch off LED
   BCF   PORTA,4       ;TEST switch off LED
   CALL   SerialReceive

   XORLW   '#'     ;Wait for # character.
   BTFSS   STATUS,Z
   GOTO   Wait_Rx_Chr   ; Not # character

   MOVLW   RX_Timer   ; Number of 10 mS to wait for station ID character
   MOVWF   RX_Count


Test_DAV:
     btfsc  PIR1,RCIF  ;check if data received
   GOTO   Read_Data
   CALL   Delay_10mS
   DECFSZ   RX_Count
   GOTO   Test_DAV
   GOTO   Wait_Rx_Chr



Read_Data:

   SELBANK 3  ; SET BANK 3
     movf  RCREG,W     ;get received data into W
   BCF  RCSTA,CREN     ;disable receive

     SELBANK 0  ; SET BANK 0

   XORLW   Station_ID
   BTFSS   STATUS,Z
   GOTO   Wait_Rx_Chr   ; Not this station

   CALL   Read_ADC

   CALL   Output_Decimal     ;Output in decimal

   MOVLW   0x0D     ;C/R
   CALL   SerialTransmit

   MOVLW   0x0A     ;L/F
   CALL   SerialTransmit

   CALL Delay_100mS
   CALL Delay_100mS
   CALL Delay_100mS

   GOTO   Wait_Rx_Chr
;     --------------------------------------------------------------------------------------


; *****************************
;  Subroutines
; *****************************
          
;*****************************************************************************  
;
;  Function :  SerialTransmit
;  This function sends the byte in W over the RS232 port. The
;  function will wait until previous data has been sent
;
;  Input:  Byte in W
;
;  Output:  
;
;*****************************************************************************  
SerialTransmit:
     btfss  PIR1,TXIF  ;check that buffer is empty
     goto  $-1
     SELBANK 3  ; SET BANK 3  
     movwf  TXREG  ;transmit byte
     SELBANK 0  ; SET BANK 0
     return
;*****************************************************************************
  
SerialReceive:

     btfss  PIR1,RCIF  ;check if data received
     goto  SerialReceive  ;wait until new data
   SELBANK 3  ; SET BANK 3
     movf  RCREG,W  ;get received data into W
     SELBANK 0  ; SET BANK 0
     return
;***************************************************************************** 
  
;*Read ADC
;* Result in ADC_TempL & ADC_Temp_H
;*
;***************************************************************************** 
;
  
Read_ADC:
     SELBANK 1  ; SET BANK 1
   MOVLW   0x09     ; Channel 2, ADON (Bit 0)
   MOVWF   ADCON0
  
   NOP     ;For short delay
   NOP
   NOP
   NOP
   NOP

   BSF   ADCON0,ADGO   ;Start conversion
ADC_Tst:  
   BTFSC   ADCON0,ADGO  
   GOTO   ADC_Tst
   MOVFW   ADRESH
     SELBANK 0  ; SET BANK 0
   MOVWF   ADC_Temp_H
     SELBANK 1  ; SET BANK 1
   MOVFW   ADRESL
     SELBANK 0  ; SET BANK 0
   MOVWF   ADC_Temp_L



; Rotate left 4 bits
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   MOVLW   0xF0
   ANDWF   ADC_Temp_L   ; Clear bits 0 to 3 that could have been set by uncleared carry bit
   RETURN

;     -----------------------------------------------------------------------------



Delay_10mS:       ;Need to set param to (10000 - 14)/2  = 9986/2 = 4993 = 0x1381
   MOVLW   0x13
   MOVWF   param2
   MOVLW   0x81
   MOVWF   param1
   GOTO   delay_cycles




Delay_100mS:
   MOVLW   0xC3
   MOVWF   param2
   MOVLW   0x49
   MOVWF   param1
   GOTO   delay_cycles
  
Delay_2_Sec:
   MOVLW   D'20'
   MOVWF   Temp_1
D2_Loop:
   CALL   Delay_100mS
   DECFSZ   Temp_1
   GOTO   D2_Loop
   RETURN

;*******************************************************************************
; Function:  delay_cycles
; Description: Delay a specified number of instruction cycles including
;  interrupt cycles.  The function call overhead adds between
;  13 and 16 cycles of delay on top of the specified value.
; With 4 Mhz system clock and 1:2 prescale
;Delay will be  param * 2 uS + (13 * 1uS)  + 0 to 3 uS
;     = param * 2 uS + (13 to16.0 uS)  (Use 14 uS for calculation.)
;  So param = No. of uS/2 - 7
; Parameters:  param1 - least significant byte of 16 bit cycle delay
;  param2 - most significant byte of 16 bit cycle delay
; Returns:  None
;*******************************************************************************
delay_cycles:
  comf  param1,F  ; negate the delay by complementing the     (1 uS)    (1 cycle)
  comf  param2,F  ; low and high bytes         (1 uS)    (1 cycle)
  bcf  T1CON,TMR1ON  ; stop timer 1           (1 uS)    (1 cycle)
  movf  param1,W  ; move the low byte of the delay into     (1 uS)    (1 cycle)
  movwf  TMR1L  ; timer 1           (1 uS)    (1 cycle)
  movf  param2,W  ; move the high byte of the delay into     (1 uS)    (1 cycle)
  movwf  TMR1H  ; timer 1           (1 uS)    (1 cycle)
  bcf  PIR1,TMR1IF  ; clear the timer 1 rollover flag     (1 uS)    (1 cycle)
  bsf  T1CON,TMR1ON  ; turn on timer 1         (1 uS)    (1 cycle)
  
tmr1_check:  
  btfss  PIR1,TMR1IF  ; wait for the timer 1 rollover flag to     1 uS while looping    (2 uS) (2 cycle) on exit
  goto  tmr1_check  ; trigger
  return         ;             (2 uS)   (2 cycle)

;**********************************************************************************
; Subroutine BCD (to convert 28-bit binary to 8-digit BCD)
; Binary value is in BIN1, BIN2, BIN3 & BIN4. BIN1 is LSB, BIN4 is MSB
; Result in BCD is in BCD1, BCD2, BCD3, BCD4 & BCD5. BCD1 is for overrange,
; BCD2 is MSB, BCD5 is LSB
;**********************************************************************************

BIN_BCD:  
   bcf   STATUS,C   ; clear carry bit
   movlw   D'32'
   movwf   COUNT     ; 32 in count
   clrf   BCD1     ; set BCD registers to 0
   clrf   BCD2
   clrf   BCD3
   clrf   BCD4
   clrf   BCD5

LOOPBCD:
   rlf   BIN1,f     ; LSB shift left binary registers
   rlf   BIN2,f
   rlf    BIN3,f
   rlf   BIN4,f     ; MSB
   rlf   BCD5,f     ; LSB shift left BCD registers
   rlf   BCD4,f
   rlf   BCD3,f
   rlf   BCD2,f
   rlf   BCD1,f     ; MSB

   decfsz   COUNT,f     ; reduce count value return when 0
   goto   DECADJ     ; continue decimal adjust
  
; result in BCD1-5. (BCD1 overrange, BCD2 MS byte)

   swapf   BCD2,w     ; get ms nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   ESEVN     ; ms digit
   movf   BCD2,w     ; get 2nd ms nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   ESIX

   swapf   BCD3,w     ; get next nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   EFIVE     ; ms digit
   movf   BCD3,w     ; get next nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   EFOUR
  
   swapf   BCD4,w     ; get ms nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   ETHREE     ; ms digit
   movf   BCD4,w     ; get 2nd ms nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   ETWO

   swapf   BCD5,w     ; get ms nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   EONE     ; ms digit
   movf   BCD5,w     ; get 2nd ms nibble
   andlw   0x0F
   iorlw   0x30     ; convert to ASCII
   movwf   EZERO
   return       ; completed decimal to BCD operation

; subroutine decimal adjust

DECADJ   movlw   BCD5     ; BCD LSB address
   movwf   FSR1L     ; pointer for BCD5
   CLRF   FSR1H
   call   ADJBCD     ; subroutine to adjust BCD
   movlw   BCD4
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   movlw   BCD3
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   movlw   BCD2
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   movlw   BCD1
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   goto   LOOPBCD

; subroutine adjust BCD

ADJBCD   movlw   0x03     ; w has 03
   addwf   INDF1,w     ; add 03 to BCDx register (x is 1-5)
   movwf   TEMP     ; store w
   btfsc   TEMP,3     ; test if >7
   movwf   INDF1     ; save as LS digit
   movlw   0x30     ; 3 for MSbyte
   addwf   INDF1,w     ; add 30 to BCDx register
   movwf   TEMP     ; store w
   btfsc   TEMP,7     ; test if >7
   movwf   INDF1     ; save as MS digit
   return       ; end subroutine



;**********************************************************************************
;
;
Output_Decimal:

   CLRF   BIN4     ;Clear top 3 bytes (Not used.)
   CLRF   BIN3
   CLRF   BIN2
  
   MOVF   ADC_Temp_H,W   ;ADC high byte
   MOVWF   BIN2  

   MOVF   ADC_Temp_L,W   ;ADC low byte
   MOVWF   BIN1  

   CALL   BIN_BCD

Output_Reading:

   MOVF   EFOUR, W
     CALL  SerialTransmit  

   MOVF   ETHREE, W
     CALL  SerialTransmit  

   MOVLW   '.'
     CALL  SerialTransmit

   MOVF   ETWO, W
     CALL  SerialTransmit

   MOVF   EONE, W
     CALL  SerialTransmit


   MOVF   EZERO, W
     CALL  SerialTransmit


   MOVLW   ' '
     CALL  SerialTransmit



   MOVLW   'V'
     CALL  SerialTransmit
   MOVLW   'o'
     CALL  SerialTransmit
   MOVLW   'l'
     CALL  SerialTransmit
   MOVLW   't'
     CALL  SerialTransmit
   MOVLW   's'
     CALL  SerialTransmit
   MOVLW   ' '
     CALL  SerialTransmit



   RETURN


   end
This could be modified to carry out the action you require insted of sending data.

Les.
 
Top