Delphi - Arduino Kommunikation

Hallo,

Ich baue zur Zeit eine Computermaus mit dem Arduino. Ich möchte für einige Einstellungen am Computer dafür ein Programm erstellen. In diesem sollen die Einstellungen vorgenommen werden und dann an den Arduino gesendet werden.

Das Programm soll in Delphi geschrieben werden, da ich mich damit besser auskenne als mit anderen Programmiersprachen.

Weiß irgend jemand wie ich diese Kommunikation erstellen kann? Also so ähnlich wie in dem seriellen Monitor der Arduino IDE.

Falls ich mich irgendwie unverständlich ausgedrückt habe, was ich machen will einfach nachfragen :slight_smile:

Danke schon mal im vorraus

Viele Grüße
Max

Die beiden Enden wissen nicht mit was sie kommunizieren. Den Arduino kümmert es nur dass auch die Daten in dem Format ankommen das er erwartet. Wenn du also was im Serial Monitor eintippen und auswerten kannst, geht das auch mit einer anderen Gegenstelle.

Ich mache sowas gerne mit C Strings. Dafür gibt es hier eine Einlese-Funktion:

SERIAL_BUFFER_SIZE entsprechend größer machen! Der String muss beim Senden mit einem Linefeed abgeschlossen werden.

Beim Format hat man mehrere Optionen um festzustellen was man empfangen hat. Man kann ganze Teil-Strings wie "time:" oder "mode:" voranstellen und auf diese mit strncmp() abfragen.

Es ist aber oft einfach nur "t" oder "m" zu nehmen. Dann kann einfach ein switch/case auf das erste Zeichen machen:

switch(serialBuffer[0])
{
}

z.B. hier um eine Zeit im Format "Txx:xx" auszuwerten:

case 'T':
{
  byte hour = atoi(serialBuffer + 1);
  byte minute = atoi(serialBuffer + 4);
  ...
}
break;

Oder wenn du sowas hast: "M1,12,13" kann man das so machen:

case 'M':
{
  byte var1 = atoi(strtok(serialBuffer + 1, ","));
  byte var2 = atoi(strtok(NULL, ","));
  byte var3 = atoi(strtok(NULL, ","));
  ...
}

Kommt letztlich darauf an was du genau übertragen willst

Delphi hat auch Funktionen für zero-terminated Strings, damit kannst Du das Parsen ausprobieren und nachher für den Arduino in C übertragen. Ansonsten sind die dynamischen Delphi Strings völlig kompatibel mit C Strings (Typecast PChar(str)), sie enden immer mit einem (versteckten) Null-Byte. Die Stream Klasse (Basis für Serial) dürfte Dir als Delphi-Programmierer ja keine besonderen Rätsel aufgeben, wenn Du sie zum Parsen benutzen möchtest.

Das Finden des richtigen COM Ports ist furchtbar kompliziert, geht anscheinend nur über ene IShell Klasse. Ich würde für meine zukünftigen Experimente den Port erst mal von Hand vorgeben und warten, bis jemand den C++ Code nach Delphi portiert hat :-]

Mit der Funktion die ich oben verlinkt habe muss man kein Zero/Null Byte mitsenden. Schadet aber auch nichts. Das würde ignoriert werden und der Arduino hängt sein eigenes an :slight_smile:

Wie das Delphi-seitig aussehen muss weiß ich nicht. Damit habe ich schon lange nichts mehr gemacht und nie was mit Serial.

:slight_smile: danke für die schnellen Antworten muss ich mir später mal ansehen

PS. Ich meinte nicht das ich Delphi besonders gut kann ^^
habe das gerade in der Schule, aber halt erst seit ca einem halben Jahr, aber da ich mit C noch so gut wie gar nichts gemacht habe (nur arduino was ich grad auch in der Schule habe) hatte ich mich zuerst für Delphi interessiert.

Was mich vor allem interessiert, ist wie man mit Delphi seriell kommuniziert und eben das was DrDiettrich gerade auch schon erwähnte mit dem richtigen Port.

Für den Anfang kann man natürlich wie in Arduino den Port auswählen, aber auf Dauer ist das halt nicht besonders schön.

Sollte das Ganze in C einfacher sein kann ich es auch damit ausprobieren. Allerdings muss ich mir da dann halt erst anschauen wie überhaupt das "graphische" Grundlagenzeug wie Buttons,edit ... das ich in der Delphi IDE schon kenne. Falls ich dies machen sollte könnte es auch nicht Schaden wenn jemand zufällig ne Internetseite kennt, auf der Ich das ganze graphische mal anschauen kann.

Wenn du was anderes willst, dann würde ich zu C# (C Sharp) und WindowsForms raten (WPF ist neuer aber komplizierter). Das hat alles integriert und man braucht nicht noch irgendwelche Toolkits für das GUI.

Da müsstest du dich halt erst mal einarbeiten. Anleitungen gibt es massig, aber es kostet Zeit.

Delphi ist völlig ok, wenn du das machen willst.

Ok dann werde ich es wohl erst mal mit Delphi probieren und dann später wenn nötig zu einer anderen Programmiersprache greifen.

Weiß jemand wie genau man das mit Delphi machen kann? Oder unter was ich suchen könnte?
Habe mich auch schon ein bisschen umgeschaut und zB dieses Beispiel sieht mir schon ganz gut aus, allerdings will ich das ja auch lernen und nicht nur stumpfsinnig irgend was eintippen und hoffen das es funktioniert o.0
Verstehe noch nicht so ganz was genau da gemacht wird (also sieht mir aus wie port konfigurieren am Anfang, dann Daten versenden und beim schließen wieder port schließen, aber zum Einen kann ich nicht beurteilen ob das so richtig ist in dem Beispiel und ob ich das richtig verstehe )

{*
 * Delphi Temperature Logger
 * -----------------
 * Logs the temperature aquired on An arduino Board using the LM35 Precision
 * centigrade temperature sensor connected to Analog Pin 0 thru the serial comm
 *
 * Created April 03 2009
 * copyleft 2009 Roberto Ramirez <beta@thepenguincult.com>
 * Full Source code at http://www.thepenguincult.com/proyectos/arduino-delphi-lm35/
 *
 *}



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    btn_connect: TButton;
    StatusBar1: TStatusBar;
    lbl_temp: TLabel;
    lbl_degree: TLabel;
    lbl_celsius: TLabel;
    Timer1: TTimer;
    ComboBox1: TComboBox;
    procedure btn_connectClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    function OpenCOMPort: Boolean;
    function SetupCOMPort: Boolean;
    procedure SendText(s: string);
    function ReadText: string;
    procedure CloseCOMPort;
    procedure Button1Click(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
  private
  ComFile: THandle;
  DCB: TDCB;
  myComPort:String;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//ComPort öffnen mit gewählten Port zuweisen
function TForm1.OpenCOMPort: Boolean;
 var
   DeviceName: array[0..80] of Char;

 begin
   StrPCopy(DeviceName, myComPort+':');
  ComFile := CreateFile(DeviceName,
     GENERIC_READ or GENERIC_WRITE,
     0,
     nil,
     OPEN_EXISTING,
     FILE_ATTRIBUTE_NORMAL,
     0);
 
  if ComFile = INVALID_HANDLE_VALUE then
     Result := False
   else
     Result := True;
 end;

 //ComPort einrichten
 function TForm1.SetupCOMPort: Boolean;
 const
   RxBufferSize = 256;
   TxBufferSize = 256;
 var
   Config: string;
   CommTimeouts: TCommTimeouts;
 begin
  Result := True;
  if not SetupComm(ComFile, RxBufferSize, TxBufferSize) then
     Result := False;
 
  if not GetCommState(ComFile, DCB) then
     Result := False;

   Config := 'baud=9600 parity=n data=8 stop=1';
 
  if not BuildCommDCB(@Config[1], DCB) then
     Result := False;
 
  if not SetCommState(ComFile, DCB) then
     Result := False;
 
  with CommTimeouts do
   begin
     ReadIntervalTimeout         := 0;
     ReadTotalTimeoutMultiplier  := 0;
     ReadTotalTimeoutConstant    := 1000;
     WriteTotalTimeoutMultiplier := 0;
     WriteTotalTimeoutConstant   := 1000;
   end;
 
  if not SetCommTimeouts(ComFile, CommTimeouts) then
     Result := False;
 end;
 
//Text über Comport senden
procedure TForm1.SendText(s: string);
 var
   BytesWritten: DWORD;
 begin
   //s := s + #13 + #10;
   WriteFile(ComFile, s[1], Length(s), BytesWritten, nil);
 end;

//Text vom Comport lesen
function TForm1.ReadText: string;
 var
    //!!!!
    //In unserem Beispiel lesen wir immer 6 Zeichen, da vom Arduino auch 6 Zeichen gesendet werden
    //!!!!
   d: array[1..6] of Char;
   s: string;
   BytesRead:cardinal; i: Integer;
 begin
   Result := '';
   if not ReadFile(ComFile, d, SizeOf(d), BytesRead, nil) then
   begin
     { Raise an exception }
   end;
   s := '';
   for i := 1 to BytesRead do s := s + d[I];
   Result := s;
 end;
 
//ComPort schliessen
procedure TForm1.CloseCOMPort;
 begin
  try
   if  GetCommState(ComFile, DCB) then
  CloseHandle(ComFile);
  finally
    timer1.Enabled:=false; 
  end;
 end;

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
myComPort := Combobox1.text;
statusbar1.Panels[0].Text:='Port in use ' + myComPort;
end;

procedure TForm1.btn_connectClick(Sender: TObject);
begin
  if btn_connect.caption = 'Disconnect' then
  begin
  btn_connect.Caption:='Connect';  // Toggle the caption of Connection Button
  combobox1.Enabled := true;;
  CloseCOMPort;
  statusbar1.Panels[1].Text:='Disconnected';  // Status bar is set to display connection info
  lbl_temp.Caption:='0';            // We also reset the temp label to zero
  end else begin
  btn_connect.Caption:='Disconnect';  // Toggle the caption of Connection Button
  combobox1.Enabled := false;
  timer1.Enabled:=true;
  Opencomport;
  SetupComport;
  statusbar1.Panels[1].Text:='Connected';  // Status bar is set to display connection info
  lbl_temp.Caption:='0';
  end;                 
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  myComPort:= 'COM8';
  Combobox1.text := myComPort;
  statusbar1.Panels[0].Text:='Port in use ' + myComPort;  // Status bar is set to display predefined Port in use at begining of program execution
  statusbar1.Panels[1].Text:='Disconnected'
end;


procedure TForm1.Timer1Timer(Sender: TObject);
var
buffer: string;
bufferF:double;
TF: TFormatSettings;
begin
  //Text vom Comport in String lesen
  buffer := readtext;
  if Length(buffer) > 0 then
    begin
    lbl_temp.Caption:=buffer;
    TF.DecimalSeparator := '.';
    bufferF := StrToFloat(buffer,TF);
    if bufferF>25 then
      SendText('1')
      else
      SendText('2');
    end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
      timer1.Enabled:=false;
     // ComPort1.Close;
     closecomport;
end;

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

end.

Du must nichts anderes suchen, mit Delphi geht sowas prima.

Du braucht für Delphi eine serielle Komponente, die ist von Haus aus nicht dabei.

Gibt es aber als Freeware.
Entweder gleich in der IDE fest den Comport und Baudrate einstellen, oder in der Anwendung flexibel machen.

Diese seriellen Komponenten sind recht einfach zu nutzen, es gibt meist eine onReceive Callback Funktion.
Wird immer angesprungen wenn Daten kommen.
Oft sind auch aussagekräftige Beispielprogramme dabei.

Nachtrag: Ich sehe gerade, du hast schon eine serielle Komponente ?, wenn sich dein Beispiel compilern lässt.

Ja das Programm lässt sich kompilieren. Was genau daran ist jetzt diese serielle Komponente?

Und kann man herausfinden an welchem comport der Arduino hängt? In dem Beispiel kann der Nutzer den Comport wählen, aber praktischer wäre es natürlich wenn direkt der richtige gewählt würde.

Es gibt auch ein Delphi Forum :wink:

Dein Beispielcode verwendet keine serielle Komponente, dies wird hier zu Fuß erledigt.
Nicht gleich gesehen mein Fehler.

Einfacher verständlich wäre für dich, wenn du dir eine serielle Komponente installierst.

Must halt googlen.

An welchem Comport dein Arduino hängt, sagt dir der Gerätemanger von Windoof

In dem Beispiel kann der Nutzer den Comport wählen, aber praktischer wäre es natürlich wenn direkt der richtige gewählt würde.

Hier wird der Comport gesetzt. myComPort

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
myComPort := Combobox1.text;
statusbar1.Panels[0].Text:='Port in use ' + myComPort;

Wenn du das beim Start gleich richtig haben willst dann hier:

procedure TForm1.FormCreate(Sender: TObject);
begin
  myComPort:= 'COM8';
  Combobox1.text := myComPort;
  statusbar1.Panels[0].Text:='Port in use ' + myComPort;  // Status bar is set to display predefined Port in use at begining of program execution
  statusbar1.Panels[1].Text:='Disconnected'
end;


end;

Hallöchen,

mehr oder weniger zufällig habe ich heute auch noch einmal so etwas gesucht. Gefunden habe ich es hinter nachfolgendem Link. Ganz unten auf der Seite ist der Download einer “RSCOM.DLL”. Dahinter verbergen sich einige ZIPs, die man sogar OHNE Einbindung einer neuen Komponente zum Schnurren bringen kann. Zumindest läuft es unter Delphi 6 und zeigt genau das im Arduino-LCD an was ich haben wollte. Für individuelle Dinge müsste man ein wenig hinzu fummel :wink:

http://www.elektronik-labor.de/RS232/RS232_3_6.htm

Gruß, Rudi

Danke,
das sieht schon ziemlich gut aus :slight_smile:

Habe allerdings noch Fragen zu den Funktionen.

Was wird durch die Einstellungen Parity, Bits, Stop und Puffer bewirkt?

Grüße
Max

Schau vielleicht bei Wikipedia, dort ist alles viel besser erklärt als man das hier eventuell könnte. Wichtig ist doch zunächst, dass dein Sender und Empfänger auf die selben Einstellungen getrimmt werden. Sonst funkt nix ...

Der Arduino arbeitet normal mit 8N1. 8 Datenbits. 1 Stop-Bit. Kein Parity Bit:
https://www.arduino.cc/en/Serial/Begin

Wenn du auf dieser Ebene mit Serial arbeitest solltest du aber wirklich grundlegend verstehen was da abläuft.

:slight_smile: ja sollte ich wohl
deshalb will ich es ja lernen, in der Schule hatten wir zu Serial halt nur Serial.begin und halt read und write.

Es geht ganz allgemein darum wie das elektrisch aussieht. Das ist völlig unabhängig von der Software oder dem System:

Das Parity Bit ist optional und normal nicht vorhanden. Dient der Fehlererkennung:

Noch eine Frage ^^

Ich möchte meinen arduino mit dem Pc über bluetooth verbinden.
Dieses Modul habe ich mir da angeschaut.

Zum einen die Frage ob es geeignet ist (will zum einen Daten an den Arduino senden und zweitens Maus und Keyboard Befehle an den PC senden) und zum zweiten, brauch ich da zwei oder reicht eines wenn mein Computer Bluetooth hat?

Zumindest läuft es unter Delphi 6 und zeigt genau das im Arduino-LCD an was ich haben wollte. Für individuelle Dinge müsste man ein wenig hinzu fummel :wink:

Hallo Namensvetter, wenn er eine Delphi Komponente installiert wird es für ihn leichter zu verstehen sein.
Dort liegt meine Meinung nach der Vorteil.

Man zieht das Icon der Kompo auf die Form, im Objektinspektor kann man das parametieren, und die Funktionen einhängen.

Grüße Rudi

'nabend Namensvetter,
da hast du auf jeden Fall Recht. Mit einer Komponente ist das alles mit Abstand viel einfacher und übersichtlicher. Ich kenne Delphi schon seit der Version 2 und habe selbst schon Komponenten erstellt. Das Beispiel von mir war ja auch nur für den Notfall und den schnellen Einsatz gedacht. Viel wichtiger ist aber, dass der Kollege versteht, was mit Parity, Start- und Stopbit los ist. Und da muss er wohl noch einiges erforschen. Den Komponenten sieht man halt nicht an was innen werkelt …
Gruß, Rudi