### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

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

• 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

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 connections
var 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 connections

var LCD_Backlight : sbit at PortD7_bit; // Display Power ON
var Beeper        : sbit at PORTD4_bit;

VAR str_length, empfang : WORD;
laenge : WORD;
//Output_str : String[255];
Output_str : array [0..255] of char;
delim: array [0..3] of char;
output_byte : array[0..255] of byte;

Begin
Begin
inc(lt);
end;
end;

begin
{ Main program }

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);

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 »
Wind XP / 7 / 10

• 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:-)
Wind XP / 7 / 10

• Hero Member
• Posts: 12194
##### 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.

• 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?
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

• 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 connections
var 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 connections

var LCD_Backlight : sbit at PortD7_bit; // Display Power ON
var Beeper        : sbit at PORTD4_bit;

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;

begin
inc(timer);
end;

Begin
Begin
inc(lt);
timer:=0;
end;
end;

//******************************************

begin
delay_ms(1);
while ADCSRA.6 = 1 do nop;
end;
//******************************************

begin
{ Main program }

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);

{ 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 ennable
delay_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.
Wind XP / 7 / 10

#### y.ivanov

• Hero Member
• Posts: 554
##### 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.
6. var
7.   b: byte;
8.   Begin
9.    while (UART1_Data_Ready() = 1) do // use 'while' when FIFO
10.    Begin
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: 664
##### 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]
Code: [Select]
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.