Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Comunicaciones RS232 - Tratamiento de la información. (https://www.clubdelphi.com/foros/showthread.php?t=53632)

jplj 25-02-2008 23:41:19

Comunicaciones RS232 - Tratamiento de la información.
 
Hola:

Empelando el componente Async Professional (Apro) trato de obtener los datos que proporciona una la estación meteorológica WST 7000C.

La estructura del mensaje transmitido por la estación la detallo al final del post.

He pensado emplear el componente ApdDataPacket para leer como un paquete completo, creo haberlo configurado correctamente y en su evento OnPacket pretendo leer el contenido.

En este evento se recibe como argumentos:
Data: pointer
Size: integer

Mi problema está como no puede ser de otra forma en mis escasos conocimientos. Mi pregunta es ¿cómo obtengo de Data cada uno de los bytes que componen el mensaje y los transformo en el su valor adecuado?


Código Delphi [-]
Byte nº 1
El primer byte transmitido indica que la sonda está empezando a enviar un nuevo mensaje.
El valor de este byte es fijo en el 85 decimal.

Byte nº 2
Informa sobre la "longitud del mensaje" que falta por recibir, su valor depende del tipo de sonda que envía el mensaje.
El valor en mi caso es 18 decimal.

Byte nº 3
Este es el byte del sistema automático interno de control de validación de datos.
El valor de cada bit del byte depende de la validez de la medición de cada uno delos parámetros meteorológicos.
El valor lógico "1" indica que la medición es válida.
El valor lógico "0" indica que la medición no es válida.
Para una función no puesta en marcha, el bit que representa se coloca al valor lógico "1".

Tabla de autotest

Bit número  Función Observada 
O LSB           Velocicidad del viento
1    Dirección del viento
2    Temperatura del aire
3    Presión barométrica
4     Humedad relativa
5    Punto de rocio
6    Ninguna - Puesto a 1
7 MSB    Ángulo medido por la brújula.
    
  
Bytes nº 4 a xx “Valores de los parámetros meteorológicos”
Cada parámetro meteorológico se transmite con un formato de 2 bytes.
12 bits se utilizan para representar los números enteros y 4 bits para las fracciones del número entero. La estructura de los bytes se representa a continuación.
El primer byte transmitido representa 8 de los 12 bits usados para representar el entero.
El segundo byte transmitido utiliza los primeros 4 bits para representar el resto de los enteros y los cuatro últimos bits para representar las fracciones.
El bit menos significativo (LSB) tiene un valor de 1/16º de la unidad de tamaño medida.
Los números negativos se expresan usando el valor 2.


              High byte                              Low byte
     ----------------------------------------- --------------------------- 
MSB> 15 | 14 | 14 | 13 | 12 | 11 | 10 | 9 | 8 || 7 | 6 | 5 | 4 | 3 | 2 | 1  < LSB


Cálculo del valor de dos bytes = ((Byte alto * 256) + (Byte bajo))/16

Una velocidad del viento de  22,75 m /s   se codifica 016C Hex
Una dirección del viento de   22,00 grados   se codifica 0160 Hex
Una temperatura de     -22,75 °C   se codifica FE94 Hex
Una presión de       1013 hPa   se codifica 3F50 Hex



Bytes nº 16 & 17
Tipo de sonda  Función medida     Metodología de decodificación
WST 7000 C   Ángulo entre el norte   Según el párrafo 4.3.4
    y el acimut 0°

Bytes Nº 18 y 19 Entrada analógica externa
Los bytes 18 y 19 se usan para transmitir la representación en 12 bits de una tensión analógica en el rango de 0 a +10 VCC.
El byte 18 transmite los 4 bits más significativos. El byte 19 transmite los 8 bits menos significativos.
La resolución es 10 V / 212 es decir, 2,4414 mV.

Último byte del mensaje, control de validez de los datos
El último byte transmitido es el de la "Checksum". Su valor se calcula tomando el "OR Exclusivo" de todos los bytes de mensaje transmitidos, excepto el último.
El mensaje recibido es válido si esta "Checksum" es idéntica al último byte transmitido.


Guía completa: http://juanpedro.l.googlepages.com/Manual.pdf


Muchas gracias de antemano
Un saludo
Juan P.

egostar 25-02-2008 23:51:47

Por lo que comentas los paquetes de datos me parece que estan representados de esta forma:

Cita:

<STX> DATOS <ETX> <BCC>
Si las cadenas tienen una longitud fija, no deberias de tener problemas para obtener la información al indicar el número de caracteres esperados y usando el evento OnPacket.

El CheckSum <BCC> es un simple XOR (OR exclusivo) desde el primer caracter <STX> hasta el caracter anterior al <ETX>.

Si pudieras mostrar una cadena ejemplo podriamos ayudarte con mas certeza.

Salud OS.

PD, el link que pones no se puede acceder.

jplj 25-02-2008 23:58:43

Ahora no puedo poner los datos porque no tengo el equipo conmigo, mañana pondré lo que me sale, y también el código que empleo.

El link está dirigido a un pdf y efectivamente no funciona, no sé. Si copias y pegas la dirección en el navegador sí accedes al pdf.

Crandel 26-02-2008 00:35:05

Por lo que entiendo tenes tu evento OnPacket donde recibis los datos, ahi copias el contenido a un buffer interno o lo procesas directamente (preferible lo primero)

Código Delphi [-]
procedure CargarDatos(Data : PByte)
var 
....
begin
  // tengo datos nuevos
  if (Data[0] <> 85) then Exit; 
  // verifico que sean los datos de compatible al modelo que tengo
  if (Data[1] <> 18) then Exit;
  // Verifico que los datos sean validos
  if (Data[2] <> 1) then Exit;
  // Vel veinto
  FVelViento = WordToFloat(Data[3] * 16 + Data[4]);
 ....
end;

esto es una idea de lo que tenes que hacer.

falta definir tambien tu funcion WordToFloat que es la que te transforma tu formato de dato.

Espero que te ayudea empezar

jplj 26-02-2008 11:17:26

Hola:

En la línea que indica Crandel he hecho el procedimiento que listo más abajo. La salida del procedimiento también la listo a continuación.

He hecho algunas modificaciones:

En :
Código Delphi [-]
if (Data[0] <> 85) then Exit;

Se producía un error al compilar
Cita:

Array type required
De ahí :

Código Delphi [-]
var
   datos: PChar;
....
begin
....
   datos:= Data;

También modifiqué la formula para obtener los datos.

Código Delphi [-]
  // La que propone Crandel:
  FVelViento = WordToFloat(Data[3] * 16 + Data[4]);

  // La que he hecho:
   FVelViento:= ((Ord(datos[ 3])*256) + Ord(datos[ 4]))/16;

Creo que los datos que obtengo son correctos, tengo que contrastarlos con los de otra estación meteorológica para verlo.
Agradecería que me dijerais si encontráis algún error.

Si todo fuera correcto, me quedan dos preguntas:

1.- Determinar el resultado de la autoverificación:
A la luz de los resultado que obtengo para el 2 byte, cualquier valor diferente a 255 indicaría un error. Pero quería saber cómo puedo determinar cuál es en base a la tabla de auto-test.

2.- Control de validación "Checksum".
¿Cómo implemento en pascal la verificación del paquete?


Código Delphi [-]

procedure TForm1.ApdDataPacket1Packet(Sender: TObject; Data: Pointer;
  Size: Integer);
var
   datos: PChar;
   s: string;
   FVelViento: Double;
   FDirViento: Double;
   FTempAire : Double;
   FPresion  : Double;
   FHumRelat : Double;
   FPuntRocio: Double;
   FAzi_Nort : Double;

begin

   datos:= Data;
  if (datos[0] <> #85) then Exit else s:= 'Inicio ';
  // verifico que sean los datos de compatible al modelo que tengo
  if (datos[1] <> #18) then Exit else s:= s + ' WST ';

  // Verifico que los datos sean validos
  s:= s + #13#10 + 'byte nº  2: ' +IntToStr(Ord(datos[2]));

  s:= s + #13#10 + 'byte nº  3: ' +IntToStr(Ord(datos[3]));
  s:= s + #13#10 + 'byte nº  4: ' +IntToStr(Ord(datos[4]));
  s:= s + #13#10 + 'byte nº  5: ' +IntToStr(Ord(datos[5]));
  s:= s + #13#10 + 'byte nº  6: ' +IntToStr(Ord(datos[6]));
  s:= s + #13#10 + 'byte nº  7: ' +IntToStr(Ord(datos[7]));
  s:= s + #13#10 + 'byte nº  8: ' +IntToStr(Ord(datos[8]));
  s:= s + #13#10 + 'byte nº  9: ' +IntToStr(Ord(datos[9]));
  s:= s + #13#10 + 'byte nº 10: ' +IntToStr(Ord(datos[10]));
  s:= s + #13#10 + 'byte nº 11: ' +IntToStr(Ord(datos[11]));
  s:= s + #13#10 + 'byte nº 12: ' +IntToStr(Ord(datos[12]));
  s:= s + #13#10 + 'byte nº 13: ' +IntToStr(Ord(datos[13]));
  s:= s + #13#10 + 'byte nº 14: ' +IntToStr(Ord(datos[14]));
  s:= s + #13#10 + 'byte nº 15: ' +IntToStr(Ord(datos[15]));
  s:= s + #13#10 + 'byte nº 16: ' +IntToStr(Ord(datos[16]));
  s:= s + #13#10 + 'byte nº 17: ' +IntToStr(Ord(datos[17]));
  s:= s + #13#10 + 'byte nº 18: ' +IntToStr(Ord(datos[18]));
  s:= s + #13#10 + 'byte nº 19: ' +IntToStr(Ord(datos[19]));
  s:= s + #13#10 + 'byte nº 20: ' +IntToStr(Ord(datos[20]));

//  FVelViento:= (Ord(datos[3]) * 16 + Ord(datos[4]));

   FVelViento:= ((Ord(datos[ 3])*256) + Ord(datos[ 4]))/16;
   FDirViento:= ((Ord(datos[ 5])*256) + Ord(datos[ 6]))/16;
   FTempAire := ((Ord(datos[ 7])*256) + Ord(datos[ 8]))/16;
   FPresion  := ((Ord(datos[ 9])*256) + Ord(datos[10]))/16;
   FHumRelat := ((Ord(datos[11])*256) + Ord(datos[12]))/16;
   FPuntRocio:= ((Ord(datos[13])*256) + Ord(datos[14]))/16;
   FAzi_Nort := ((Ord(datos[15])*256) + Ord(datos[16]))/16;

{  if (Ord(datos[2]) <> 1) then Exit else s:= s + IntToStr(Ord(datos[2]));;
  // Vel veinto
  FVelViento = WordToFloat(Data[3] * 16 + Data[4]);
 ....}

   Memo1.Lines.Add('Paquete: '+s);
   Memo1.Lines.Add('Velocidad Viento   : '+ FloatToStr(FVelViento));
   Memo1.Lines.Add('Dirección Viento   : '+ FloatToStr(FDirViento));
   Memo1.Lines.Add('Temperatura        : '+ FloatToStr(FTempAire));
   Memo1.Lines.Add('Presión            : '+ FloatToStr(FPresion));
   Memo1.Lines.Add('Humedad relativa   : '+ FloatToStr(FHumRelat));
   Memo1.Lines.Add('Punto Rocio        : '+ FloatToStr(FPuntRocio));
   Memo1.Lines.Add('Angulo Azimut-Norte: '+ FloatToStr(FAzi_Nort));


   Memo1.Lines.Add('-------------------------------------------------');

end;


Cita:

Salida del procedimiento:
Paquete: Inicio WST
byte nº 2: 255
byte nº 3: 0
byte nº 4: 3
byte nº 5: 19
byte nº 6: 95
byte nº 7: 1
byte nº 8: 132
byte nº 9: 62
byte nº 10: 94
byte nº 11: 3
byte nº 12: 51
byte nº 13: 0
byte nº 14: 215
byte nº 15: 9
byte nº 16: 97
byte nº 17: 0
byte nº 18: 66
byte nº 19: 223
byte nº 20: 1
Velocidad Viento : 0,1875
Dirección Viento : 309,9375
Temperatura : 24,25
Presión : 997,875
Humedad relativa : 51,1875
Punto Rocio : 13,4375
Angulo Azimut-Norte: 150,0625
-------------------------------------------------
Nuevamente muchas gracias por el tiempo que me dedicáis.
Un Saludo
Juan Pedro.

Crandel 26-02-2008 12:15:18

parece que vas avanzado bien ;)

Cita:

Empezado por jplj (Mensaje 268649)
También modifiqué la formula para obtener los datos.

Código Delphi [-]
  // La que propone Crandel:
  FVelViento = WordToFloat(Data[3] * 16 + Data[4]);

  // La que he hecho:
   FVelViento:= ((Ord(datos[ 3])*256) + Ord(datos[ 4]))/16;

perdon por explicarte bien la funsión WordToFloat o como la quieras llamar. lo que pasa es que la parte enterea solo tiene 4 bits en la parte baja, por eso el producto x 16 y no 256. Aun asi tambien esta mal lo que escribi.

La conversion correcta seria:

Código Delphi [-]
function WordToFloat(byteAlto, byteBajo: byte): double;
var
  val: double;
begin
  val := byteAlto * 16 + byteBajo div 16;
  val := val + (byteBajo and $0F) / 10000;
  Result := val;
end;

Cita:

Empezado por jplj (Mensaje 268649)
1.- Determinar el resultado de la autoverificación:
A la luz de los resultado que obtengo para el 2 byte, cualquier valor diferente a 255 indicaría un error. Pero quería saber cómo puedo determinar cuál es en base a la tabla de auto-test.

para saber cual es el error debes fijarte en el bit correspondiente a cada uno, por ejemplo:

Código Delphi [-]
  if (datos[2] and $01) <> 0 then 
   //  vel del viento correcta

Cita:

Empezado por jplj (Mensaje 268649)
2.- Control de validación "Checksum".
¿Cómo implemento en pascal la verificación del paquete?

debes hacer un ciclo for para todos datos de tu paquete haciendo xor entre ellos y comparar el resultado con el byte del checksum

jplj 28-02-2008 11:33:57

Hola:

Ya he resuelto los problemas, pero surgen otros:

En relación a la verificación del autotest, la sentencia que propones:
Código Delphi [-]
if (datos[2] and $01) <> 0 then
genera un error al compilar:
Cita:

Operator not applicable to this operand type
Finalmente lo he resuelto de la siguiente forma:
Código Delphi [-]

FAutoTest: Array[0..7] of integer;

...

   procedure AutoTest(valor:integer);
   var
      i:integer;
   begin
      i:= 0;
      while i < 8 do
      begin
         if ((1 shl i) and valor) > 0 then
            FAutoTest[i]:= 1
         else
            FAutoTest[i]:= 0;
         inc(i);
      end;
    end;

var

    ....

begin
    ...
     // 3.- Verificación del autotest.
   if (Ord(datos[2])) <> 255 then
   begin
      AutoTest((Ord(datos[2])));
      Exit;
   end;

Luego se puede comprobar los valores de FAutoTest.

El problema que tengo ahora es con los valores negativos de las magnitudes, en concreto con la temperatura:
Cita:

Bytes nº 4 a xx “Valores de los parámetros meteorológicos”
Cada parámetro meteorológico se transmite con un formato de 2 bytes.
12 bits se utilizan para representar los números enteros y 4 bits para las fracciones del número entero. La estructura de los bytes se representa a continuación.
El primer byte transmitido representa 8 de los 12 bits usados para representar el entero.
El segundo byte transmitido utiliza los primeros 4 bits para representar el resto de los enteros y los cuatro últimos bits para representar las fracciones.
El bit menos significativo (LSB) tiene un valor de 1/16º de la unidad de tamaño medida.

Los números negativos se expresan usando el valor 2. :confused:


High byte Low byte
----------------------------------------- ---------------------------
MSB> 15 | 14 | 14 | 13 | 12 | 11 | 10 | 9 | 8 || 7 | 6 | 5 | 4 | 3 | 2 | 1 < LSB


Cálculo del valor de dos bytes = ((Byte alto * 256) + (Byte bajo))/16

Una velocidad del viento de 22,75 m /s se codifica 016C Hex
Una dirección del viento de 22,00 grados se codifica 0160 Hex
Una temperatura de -22,75 °C se codifica FE94 Hex
Una presión de 1013 hPa se codifica 3F50 Hex

¿Qué significa "Los números negativos se expresan usando el valor 2"?



Nuevamente muchas gracias por el tiempo que me dedicáis.
Un Saludo
Juan Pedro.

Crandel 28-02-2008 13:30:07

Perdon por pasarte los codigos sin verificar. Es que no tengo Delphi en el trabajo.

Por un lado la comprobacion seria:

Código Delphi [-]
if (Ord(datos[2]) and $01) <> 0 then}

y la funcion de conversion correcta es:

Código Delphi [-]
function WordToFloat(byteAlto, byteBajo: byte): double;
var
  val: Smallint;
begin
  val := (byteAlto * 256 + byteBajo);
  Result := val  / 16;
end;


La franja horaria es GMT +2. Ahora son las 00:47:24.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi