Autor Beitrag
j-a-n@gmx.de
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 84


Delphi 7
BeitragVerfasst: Do 07.07.05 09:25 
Auch mal ein kleines Tutorial von mir, da ich die Anforderung hatte, lange gesucht habe und dann doch ganz erfolgreich gelöst habe. Hier mein Weg:

Einleitung
Ziel ist es, die serielle Schnittstelle zum Datenübertragen zu benutzen. Speziell bei mir geht es darum, aus einer Paketwaage das Gewicht zu lesen.
Erste Anlaufstelle und Suche war natürlich www.delphi-forum.de und www.dsdt.info. Mein erster Versuch mit den Beispielen von user profile iconSchelmVomElm ist leider gescheitert. Also bin ich weiter auf die Suche gegangen und habe SerialNG von Domis gefunden: www.domis.de/serialng.htm Das ist Freeware mit Quelltext. Und damit hat es sofort funktioniert.


Die Komponente SerialNG ist Objektorientiert und beinhaltet als Main-Klasse TSerialPortNG. Nach dem Instanziieren wird Port und Baud-Rate eingetragen und aktiviert. Die Klasse erstellt sofort einen Thread, welcher im Hintergund die Schnittstelle permanent liest. Ausserdem gibt es ein entsprechendes Event, welches den Empfang von Daten signalisert. Der Datenempfang erfolgt also permanent in einen Puffer, welchen man als Benutzer einfach abfragen kann.

Die Komponente SerialNG kann auch als No-Gui-VCL Komponente installiert werden, die entsprechenden Anleitungen sind auf der Homepage. Ich habe mir das aber alles einfach gespart, die serialng.pas ins Projekt aufgenommen, im uses eingebunden und los.

Soviel zur Theorie, nun zur Praxis:
Ich gehe davon aus, dass Du ein Projekt hast, bei dem Du irgendwann auf die Stelle triffst, "jetzt will ich Daten an COM1 schicken" bzw. "jetzt sollte ich die Daten von COM1 haben".

1. COM1 öffnen und schliessen
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure machwas;
var
  port: TSerialPortNG;
begin
  // schnittstelle aufmachen
  port := TSerialPortNG.Create(nil);  // objekt kerstellen
  try
    port.CommPort := 'COM1';  // comport einstellen
    port.BaudRate := 9600;  // baud-rate einstellen
    port.Active := true;  // schnittstelle öffnen

    machwas2(port);

  finally
    // schnittstelle zumachen
    port.Active := false;
    freeandnil(port);
  end;
end;


2. Daten an Schnittstelle senden
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure machwas2(port: TSerialPortNG);
var
  text: String;
begin
  text := 'gib mir dein gewicht';
  port.SendString(text + #13);

  machwas3(port);
end;


3. Daten empfangen
Daten empfangen geht auf zwei Wegen.
a) man registriert eine Funktion am Event OnRxClusterEvent oder
b) man wartet selbst auf die Daten.

a) eignet sich für z.B. einen Chat, wo beide Seiten wild durcheinander senden.
b) ist eher für einen Server nach dem Model: ich schicke einen Befehl und warte auf die Antwort. Das ist das, was wir hier tun.

Ach so: Cluster ist einfach nur ein Name für einen Brocken empfangener Daten. Ich habe den Namen nicht ausgesucht, ist aber nichts gefährliches.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
procedure machwas3(port: TSerialPortNG);
var
  buffer: String;
  timeout: integer;
  abgelaufen: TDateTime;
  datenok: boolean;
begin
  buffer := '';
  datenok := false;
  timeout := 2000 // zwei sekunden auf daten warten, danach abbrechen.
  abgelaufen := IncMilliSecond(getTime(), timeout); // zeit berechnen, wann timeout erreicht.

  while (getTime() <= abgelaufen) and (not datenok) do begin

    if port.nextClusterSize > 0 then begin // es sind daten (cluster) angekommen
      buffer := buffer + port.ReadNextClusterAsString(); // daten in meinen puffer lesen
    end;

    if copy(buffer, length(buffer), 1) = #13 then begin // habe genug daten erhalten
      datenok := true;
    end;

    sleep(50);
  end;

  if datenok then begin
    machwas4(buffer);
  end;
end;

Die Prozedur liesst also 2 Sekunden lang alle Daten die ankommen. Sind die Daten angekommen, die Du erwartet hast (hier: das letzte zeichen ist ein CR) hört die Schleife auf und gib die Daten weiter. Diese Methode machwas4 definieren wir mal als "vorhanden". Bei mir analysiert diese Methode das erhaltene Gewicht, konvertiert es in einen double und schreibt es in ein Formular.

Kritische Stellen
Durch die Schleife steht dein System mindestens 0,05s maximal 2s und reagiert nicht mehr. Es macht also Sinn, das ganze in einen Thread zu packen. Das war bei mir nicht nötig, da das ganze sowieso in einem Thread ist (der das Gewicht dauern liesst). Das sollte aber auch nicht schwer fallen. Aber bitte das Synchonize beim Schreiben ins Formular nicht vergesssen!

Die Bedingung "habe genug Daten erhalten" sieht so primitiv aus, hat es aber in sich. Nämlich die Frage: Woher weiss ich, wann ich genug empfangen habe. In meinem Fall was das vorgegeben, die Waage beginnt mit einem : und endet mit einem #13. Für alle anderen heisst es "Schnittstelle definieren". Möchtest Du beispielsweise zwei Rechner zusammenschliessen und Deine Daten als XML übertragen (ja, das ist möglich), wäre ein #13 ungeeignet, da das innerhalb der Daten vorkommt. Hier bietet sich eines der Steuerzeichen als Ersatz an, z.B. #0, #1, usw. Aber die Gegenstelle muss das dann aber auch senden.......

_________________
--
Dieses Werk ist unter einer Creative Commons 3.0 Lizenz lizenziert und darf unter Namensnennung kopiert, weitergegeben, veröffentlicht und verändert werden.


Zuletzt bearbeitet von j-a-n@gmx.de am Do 07.07.05 10:18, insgesamt 2-mal bearbeitet
j-a-n@gmx.de Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 84


Delphi 7
BeitragVerfasst: Do 07.07.05 09:45 
Hier noch meine ganze Unit mit dem Thread (und Waage)

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
unit UWaage;

interface

uses
  Classes, SerialNG;

type TWaage = class(TThread)
  
  private
    frunning : boolean; // gewicht in formular schreiben

    fport: String;
    fbaud: integer;
    ftimeout:integer;
    finterval: integer;
    faktiv: boolean; // waage vorhanden

    gggewicht: double;  // muss ich machen, weil synchronize keine parameter erlaubt
    procedure setGewicht;

    procedure oeffnen;
    procedure senden(port: TSerialPortNG);
    procedure lesen(port: TSerialPortNG);
    procedure analyse(buffer: String);

  protected
    procedure Execute; override;

  public
    property running:boolean read frunning write frunning ;

    property port:String read fport write fport;
    property baud:Integer read fbaud write fbaud;
    property timeout:integer read ftimeout write ftimeout;
    property interval:integer read finterval write finterval;
    property aktiv:boolean read faktiv write faktiv;
end;

implementation

uses Unit1, DateUtils, SysUtils;


  procedure TWaage.Execute;
  begin
  
    while faktiv do begin
      try
        oeffnen;
      finally
        sleep(finterval);
      end;
    end;
  
  end;


  procedure TWaage.oeffnen;
  var
    port: TSerialPortNG;
  begin
    // schnittstelle aufmachen
    port := TSerialPortNG.Create(nil);  // objekt kerstellen
    try
      port.CommPort := fport;  // comport einstellen
      port.BaudRate := fbaud;  // baud-rate einstellen
      port.Active := true;  // schnittstelle öffnen

      senden(port);

    finally
      // schnittstelle zumachen
      port.Active := false;
      freeandnil(port);
    end;
  end;


  procedure TWaage.senden(port: TSerialPortNG);
  var
    text: String;
  begin
      text := 'gib mir dein gewicht';
      port.SendString(text + #13);

      lesen(port);
  end;


  procedure TWaage.lesen(port: TSerialPortNG);
  var
    buffer: String;
    abgelaufen: TDateTime;
    datenok: boolean;
  begin
    buffer := '';
    datenok := false;
    abgelaufen := IncMilliSecond(getTime(), ftimeout); // zeit berechnen, wann timeout erreicht.

    while (getTime() <= abgelaufen) and (not datenok) do begin

      if port.nextClusterSize > 0 then begin // es sind daten (cluster) angekommen
        buffer := buffer + port.ReadNextClusterAsString(); // daten in meinen puffer lesen
      end;

      if copy(buffer, length(buffer), 1) = #13 then begin // habe genug daten erhalten
        datenok := true;
      end;

        sleep(50);
      end;

      if datenok then begin
        analyse(buffer);
      end;
  end;

  
  procedure TWaage.analyse(buffer: String);
  var
    gewicht:double;
  begin
  
    Form1.setWaageStatus(buffer); // bei mir in unit 1

    if frunning then begin // weitermachen nur wenn waage running

      if copy(buffer, 21) <> 'W' then exit; // wenn message -> noch mal versuchen
      
      if copy(buffer, 121) <> 'S' then exit; // wenn unstable -> noch mal versuchen
      
      gewicht := strtofloat(copy(buffer, 37)); // gewicht lesen
      
      if copy(buffer, 102) = 'lb' then begin // wenn lb -> umrechnen in kg
        gewicht := gewicht * 0.4535924;
      end;

      if (gewicht > 0then begin
        // gewicht zurückliefern
        gggewicht := gewicht;
        synchronize(setGewicht);

        frunning := false; // ende
      end;
    end;
  end;


  procedure TWaage.setGewicht();
  begin
    Form1.setGewicht(self.gggewicht);  // in unit 1
    gggewicht := 0;
  end;

end.

_________________
--
Dieses Werk ist unter einer Creative Commons 3.0 Lizenz lizenziert und darf unter Namensnennung kopiert, weitergegeben, veröffentlicht und verändert werden.
sneumann
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Do 06.10.11 11:28 
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, SetupAPI, StdCtrls, SerialNG;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    SerialPortNG1: TSerialPortNG;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  port: TSerialPortNG;
implementation

{$R *.dfm}
procedure machwas3(port: TSerialPortNG);
var
  buffer: String;
  timeout: integer;
  abgelaufen: TDateTime;
  datenok: boolean;
begin
  buffer := '';
  datenok := false;
  timeout := 2000 // zwei sekunden auf daten warten, danach abbrechen.
  abgelaufen := IncMilliSecond(getTime(), timeout); // zeit berechnen, wann timeout erreicht.
  while (getTime() <= abgelaufen) and (not datenok) do begin

    if port.nextClusterSize > 0 then begin // es sind daten (cluster) angekommen
      buffer := buffer + port.ReadNextClusterAsString(); // daten in meinen puffer lesen
    end;

    if copy(buffer, length(buffer), 1) = #13 then begin // habe genug daten erhalten
      datenok := true;
    end;

    sleep(50);
  end;

 // if datenok then begin
    //machwas4(buffer);
  //end;
end;

procedure machwas2(port: TSerialPortNG);
var
  text: String;
begin
  text := '0000sr 05';
  port.SendString(text + #05);

  machwas3(port);
end;

procedure machwas;
var
  port: TSerialPortNG;
begin
  // schnittstelle aufmachen
  port := TSerialPortNG.Create(nil);  // objekt kerstellen
  try
    port.CommPort := 'COM4';  // comport einstellen
    port.BaudRate := 9600;  // baud-rate einstellen
    port.Active := true;  // schnittstelle öffnen
    machwas2(port);

  finally
    // schnittstelle zumachen
    port.Active := false;
    freeandnil(port);
 end;
 end;

procedure TForm1.Button1Click(Sender: TObject);
begin
machwas();
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
machwas2(port);
end;

end.


Was genau muss jetzt in meine Unit rein die ich für mein Gerät erstellen muss? Ich habe keine Waage sondern ein Messgerät dem ich Befehle übermitteln muss.
Bei mir kommt folgende fehlermeldungen
[Fehler] Unit1.pas(38): Operator oder Semikolon fehlt
[Fehler] Unit1.pas(38): Undefinierter Bezeichner: 'IncMilliSecond'
[Fataler Fehler] progtestseriell.dpr(5): Verwendete Unit 'Unit1.pas' kann nicht compiliert werden


Ich habe jedoch noch keine WaageUnit erstellt. wie mache ich das für mein Gerät ? muss zum beispiel Strings auf das gerät senden ( befehle wie: 030 030 030 030 s73 r72 (danach Control E also 05 hex zum Connecten mit dem Gerät)
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Do 06.10.11 12:23 
Solche Sachen findet man bei

GoogleCodeSearch
www.google.com/codes...liSecond&type=cs

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
function IncMilliSecond(const AValue: TDateTime;
  const ANumberOfMilliSeconds: Int64): TDateTime;
begin
  Result := ((AValue * MSecsPerDay) + ANumberOfMilliSeconds) / MSecsPerDay;
end;
sneumann
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 07.10.11 12:05 
Oke dankeschön habe jetzt ein anderes problem weil ich seriell ng neu drauf machen musste

www.delphi-forum.de/....php?p=651861#651861
eternal
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Mo 27.08.12 15:52 
Das ist wirklich gerade sehr hilfreich für mich, danke für die Mühe!