Recent

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

coradi

  • Full Member
  • ***
  • Posts: 148
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 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 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
AVR8/ARM(STM32)

coradi

  • Full Member
  • ***
  • Posts: 148
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
AVR8/ARM(STM32)

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Find beginning of String in Uart
« Reply #2 on: September 27, 2022, 02:32:38 pm »
Most software use a property called bytesavailable
Specialize a type, not a var.

coradi

  • Full Member
  • ***
  • Posts: 148
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
AVR8/ARM(STM32)

Arioch

  • Sr. Member
  • ****
  • Posts: 421
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: 148
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;



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;
begin
inc(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 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.
Amstrad Schneider CPC 6128
AVR8/ARM(STM32)

alpine

  • Hero Member
  • *****
  • Posts: 1038
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: 845
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: 148
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
AVR8/ARM(STM32)

 

TinyPortal © 2005-2018