### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Find beginning of String in Uart (Read Smart meter ISKRA)  (Read 537 times)

#### coradi

• Full Member
• Posts: 121
##### Find beginning of String in Uart (Read Smart meter ISKRA)
« on: September 27, 2022, 01:05:16 pm »
I have a Interrupt for reading Uart
procedure Read();  iv IVT_ADDR_USART0_RX; ics ICS_AUTO;

The problem is, the string is about 200 signs long, but not always, sometime 10 oder 20 Chars shorter or longer
It begins allways with 1B 1B 1B 1B 52
The end is CRC or something, always an other number
Aber each transmission there is a delay about 1sec from the sending source.

How do I work with this?
I tought I read all, put it in an array, check if
• ..[3] is 1B and then I can work with this, but how?
Code: [Select]
`program Sun;{ Declarations section }// LCD module connectionsvar LCD_RS : sbit at PORTC0_bit;var LCD_EN : sbit at PORTC2_bit;var LCD_D4 : sbit at PORTC4_bit;var LCD_D5 : sbit at PORTC5_bit;var LCD_D6 : sbit at PORTC6_bit;var LCD_D7 : sbit at PORTC7_bit;var LCD_RS_Direction : sbit at DDC0_bit;var LCD_EN_Direction : sbit at DDC2_bit;var LCD_D4_Direction : sbit at DDC4_bit;var LCD_D5_Direction : sbit at DDC5_bit;var LCD_D6_Direction : sbit at DDC6_bit;var LCD_D7_Direction : sbit at DDC7_bit;// connect R/W to ground// End LCD module connectionsvar LCD_Backlight : sbit at PortD7_bit; // Display Power ONvar Beeper        : sbit at PORTD4_bit;var ADC_Value : word;    ADC_STR : STRING[32];VAR str_length, empfang : WORD;  laenge : WORD;  //Output_str : String[255];      Output_str : array [0..255] of char;  lt, n, read_byte : BYTE;  delim: array [0..3] of char;  output_byte : array[0..255] of byte;procedure Read();  iv IVT_ADDR_USART0_RX; ics ICS_AUTO;  Begin    IF Uart1_Data_READY THEN    Begin    output_byte[lt] := Uart1_Read();    inc(lt);    end;  end;begin  { Main program }ADC_Init();UART1_Init(9600);//UART2_Init(9600);   SREG_I_bit := 1;               // Interrupt enable   RXCIE0_bit := 1;              //UART1 Interrupt ennable  // RXCIE1_bit := 1;              //UART2 Interrupt ennable   DDRA   := 0x00;PortA  := 0x00;DDRC   := 0xFF;PortC  := 0x00;DDRD.7 := 1;LCD_Backlight:= 1;Lcd_Init();delay_ms(100);Lcd_Cmd(_LCD_CLEAR);Lcd_Cmd(_LCD_CURSOR_OFF);Lcd_out(1,1,'Initialize LCD');  delay_ms(1000);Lcd_Cmd(_LCD_CLEAR);ADC_STR:=''; For lt := 0 TO 255 DO   Output_byte[lt]:=0;  lt:=0;        While TRUE Do   Begin     IF output_byte[0] = 27 THEN     Begin     shortTostr(output_byte[198], output_str) ;     LCD_Out(2,1, Output_str);     delay_ms(100);     lt:=0;     Output_Str:='';     LCD_CMD(_LCD_CLEAR);     RXCIE0_bit  := 1;   end;      end;end.`
« Last Edit: September 30, 2022, 08:55:08 am by coradi »
Amstrad Schneider CPC 6128
Wind XP / 7 / 10

#### coradi

• Full Member
• Posts: 121
##### Re: Find beginning of String in Uart
« Reply #1 on: September 27, 2022, 02:21:04 pm »
ok, I find a solution.
Because the 1sec dely, it's more easy.

I use a timer and set a counter always to 0 in UART Interrupt
If there is not UART INT for 60ms, then the counter will set back to 0 and with enxt UART INT it knows, where the transmission starts:-)
Amstrad Schneider CPC 6128
Wind XP / 7 / 10

#### Thaddy

• Hero Member
• Posts: 12208
##### Re: Find beginning of String in Uart
« Reply #2 on: September 27, 2022, 02:32:38 pm »
Most software use a property called bytesavailable
Manuals, manuals, manuals first.
You have incompetence and sheer incompetence.

#### coradi

• Full Member
• Posts: 121
##### Re: Find beginning of String in Uart
« Reply #3 on: September 27, 2022, 02:41:00 pm »
Hm, yes, but my input Buffer is one Byte.
Another problem
if ein habe a array of byte with

1B1B1B1BA0A0A0A0  and so on.

How can I search in that?
I know to do it with string, bot how can I cehck where I find

1B1F1142 for example in a array of Byte(or HEX) with 236 fields?
Amstrad Schneider CPC 6128
Wind XP / 7 / 10

#### Arioch

• Sr. Member
• Posts: 414
##### Re: Find beginning of String in Uart
« Reply #4 on: September 27, 2022, 03:06:50 pm »
Quote
It begins allways with 1B 1B 1B 1B 52
...
1B1B1B1BA0A0A0A0  and so on.

but then, \$52 or \$A0 ?

easiest way perhaps would be like

Code: [Select]
`var   buffer: array [ ..... ] of byte;  Found: boolean;  FoundIndex, i: integer;  target: Int32;  Candidate: PInt32;begin  Found := False;  Target := \$1B1B1B1B;  for i := Low(buffer) to High(buffer) + 1 - SizeOf(target) do begin       Candidate := Point( @buffer[i] );       if Candidate^ = target then begin          Found := true;          FoundIndex := i;          break;       end;  end;end;  `
1. This does not check that \$52 or \$A0 is a next byte. This extra checks can be made on top of it, or aded to it.

2. Since this makes Int32 comparisons, this would not detect cases when buffer1 ends with  .... \$1b \$1b \$1b ] and the next buffer 2 starts with [ \$1b ...].
This means, you would have to make overlapping, when you would download the next buffer, you would haveto copy 3 (in general, SizeOf(target) - 1) last bytes of previous buffer first.

3. Boyer-Moore would be faster on large buffers, and if you only have ONE search pattern. And there on top you already quoted two different patterns, and maybe there would be more

https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm

#### coradi

• Full Member
• Posts: 121
##### Re: Find beginning of String in Uart
« Reply #5 on: September 30, 2022, 08:52:47 am »
This works for me:-)
For reading out ISKRA Smart meter
Maybe it helps some other beginner

Code: [Select]
`program Sun2000_Master;{ Declarations section }// LCD module connectionsvar LCD_RS : sbit at PORTC0_bit;var LCD_EN : sbit at PORTC2_bit;var LCD_D4 : sbit at PORTC4_bit;var LCD_D5 : sbit at PORTC5_bit;var LCD_D6 : sbit at PORTC6_bit;var LCD_D7 : sbit at PORTC7_bit;var LCD_RS_Direction : sbit at DDC0_bit;var LCD_EN_Direction : sbit at DDC2_bit;var LCD_D4_Direction : sbit at DDC4_bit;var LCD_D5_Direction : sbit at DDC5_bit;var LCD_D6_Direction : sbit at DDC6_bit;var LCD_D7_Direction : sbit at DDC7_bit;// connect R/W to ground// End LCD module connectionsvar LCD_Backlight : sbit at PortD7_bit; // Display Power ONvar Beeper        : sbit at PORTD4_bit;var ADC_Value : word;    counter_str, counter_str1, ADC_STR : STRING[32];    hex0, hex1, hex2, hex3, hex4, hex5, hex6 : STRING[3];    Wirkleistung_str : STRING[5];VAR str_length, empfang : WORD;  laenge : WORD;  Einheit : STRING[2];  //Output_str : String[255];      Output_Str, Output1_str : array [0..255] of char;  lt, timer, counter, counter1, n, read_byte : BYTE;  delim: array [0..3] of char;  output_byte : array[0..255] OF short;  wirkleistung : INTEGER;procedure Timer1Overflow_ISR(); org IVT_ADDR_TIMER1_COMPA;begininc(timer);end;procedure Read();  iv IVT_ADDR_USART0_RX; ics ICS_AUTO;  Begin   if (UART1_Data_Ready() = 1) then   Begin    output_byte[lt] := Uart1_Read();    inc(lt);    timer:=0;   end;  end;//****************************************** function Adc_Read_1(channel : byte) : word;begin ADMUX.0 := 0; ADMUX.1 := 0; ADMUX.2 := 0; ADMUX.3 := 0; ADMUX.4 := 0; ADMUX.5 := 0; ADMUX.6 := 1; ADMUX.7 := 0; ADCSRA := %11000111;delay_ms(1); ADCSRA := 128;  //199  while ADCSRA.6 = 1 do nop;  Lo(result) := ADCL;  Hi(result) := ADCH;end;//******************************************begin  { Main program }ADC_Init();UART1_Init(9600);//UART2_Init(9600);   DDRA   := 0x00;PortA  := 0x00;DDRC   := 0xFF;PortC  := 0x00;DDRD.7 := 1;LCD_Backlight:= 1;Lcd_Init();delay_ms(100);Lcd_Cmd(_LCD_CLEAR);Lcd_Cmd(_LCD_CURSOR_OFF);Lcd_out(1,1,'Initialize LCD');  delay_ms(1000);Lcd_Cmd(_LCD_CLEAR);ADC_STR:='';   { SREG_I_bit := 1;  TCCR1A := 0x80;  TCCR1B := 0x0B;  OCR1AH := 0x59;  OCR1AL := 0xFF;  OCIE1A_bit := 1;   }     //  for n:=0 to 10 DO UART1_Read();      // RXCIE0_bit  := 1; For lt := 0 TO 255 DO   Output_byte[lt]:=0;  lt:=0;    TCCR1A := 0x80;  TCCR1B := 0x0B;  OCR1AH := 0x70;  OCR1AL := 0x7F;  OCIE1A_bit := 1;     SREG_I_bit := 1;               // Interrupt enable   RXCIE0_bit := 0;              //UART1 Interrupt ennabledelay_ms(200);  While TRUE Do   Begin If TIMER >= 4 THEN      // count= X * 100ms Begin lt := 0;  RXCIE0_bit := 1; end;    bytetostr(lt,counter_str);  bytetostr(timer,counter_str1);      ByteToHEX(output_byte[193], HEX0);      ByteToHEX(output_byte[194], HEX1);  //Wirkleistung gesammt Einheit(1B=W)      ByteToHEX(output_byte[195], HEX2);      ByteToHEX(output_byte[196], HEX3);      ByteToHEX(output_byte[197], HEX4);  // Länge (52, 53 etc)    If Output_byte[194] = 0x1B THEN Einheit := 'W' ELSE Einheit := '?';        IF output_byte[197] = 0x53 THEN    Begin      Hi(wirkleistung) := output_byte[198];      Lo(wirkleistung) := output_byte[199];      end     ELSE      wirkleistung := output_byte[198];   IntToStr(Wirkleistung,Wirkleistung_Str);    ltrim(Wirkleistung_Str);    LCD_CMD(_LCD_CLEAR);    LCD_OUT(1,14, counter_str);    LCD_OUT(2,14, counter_str1);    LCD_Out(1,1, Wirkleistung_Str);    LCD_Out(1,5, Einheit);    delay_ms(10);   //  Output_Str:='';     end;end.`
Amstrad Schneider CPC 6128
Wind XP / 7 / 10

#### y.ivanov

• Hero Member
• Posts: 562
##### Re: Find beginning of String in Uart
« Reply #6 on: September 30, 2022, 12:53:22 pm »
This works for me:-)
For reading out ISKRA Smart meter
Maybe it helps some other beginner

*snip*

The timer overflow interrupt is not in sync with the UART receiver (U(A)RT stands here for 'Asynchronous') so you can end up with 4 overflows while they're closer only to three. You must take into consideration the running register of the timer to measure precisely.

Better you should scan for four 1B's like:
Code: Pascal  [Select][+][-]
1. var
2.   inmsg: boolean; // Initialize it to false
3.   cnt1b: byte; // = 0
4.
5. procedure Read();  iv IVT_ADDR_USART0_RX; ics ICS_AUTO;
6. var
7.   b: byte;
8.   Begin
9.    while (UART1_Data_Ready() = 1) do // use 'while' when FIFO
10.    Begin
11.       b := Uart1_Read();
12.
13.       // 1B1B1B1B detection
14.       if (b=\$1B) and (cnt1b < 4) then
15.       begin
16.           Inc(cnt1b);
17.           if  cnt1b = 4 then
18.           begin
19.             inmsg := true; // 1B1B1B1B detected
20.             lt := 0;
21.             Exit; // Don't buffer the 4-th 1B
22.          end
23.       end
24.       else
25.         cnt1b := 0;
26.
27.     // Buffer it only after the header
28.     if inmsg then
29.
30.       // Check there is a place into the buffer
31.       if lt <= high(output_byte) then
32.       begin
33.         output_byte[lt] := b;
34.         inc(lt);
35.       end
36.       else
37.         ;  // Don't write beyond
38.
39.    end;
40.   end;
41.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

#### ccrause

• Hero Member
• Posts: 668
##### Re: Find beginning of String in Uart
« Reply #7 on: September 30, 2022, 02:11:22 pm »
Maybe it helps some other beginner

Code: [Select]
`var LCD_RS : sbit at PORTC0_bit;`
Code: [Select]
`procedure Timer1Overflow_ISR(); org IVT_ADDR_TIMER1_COMPA;`
Code: [Select]
`procedure Read();  iv IVT_ADDR_USART0_RX; ics ICS_AUTO;`
A further note for beginners: some of the code in this thread seems to be non-standard Pascal that is not supported by FPC (perhaps mikroE extensions for their Pascal compiler?). There are FPC equivalents to implement the same functionality for the AVR target.

#### coradi

• Full Member
• Posts: 121
##### Re: Find beginning of String in Uart (Read Smart meter ISKRA)
« Reply #8 on: October 01, 2022, 02:52:58 pm »
Maybe someone liek to convert it to Freepascal?
Because I would use FPC for AVR too, mut my last try, wasn't seccussfull to install the Compiler.-(
Amstrad Schneider CPC 6128
Wind XP / 7 / 10