Kommunikation zwischen Arduino und Lazarus

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
niklasdiy
Beiträge: 3
Registriert: Sa 4. Mär 2017, 16:24

Kommunikation zwischen Arduino und Lazarus

Beitrag von niklasdiy »

Hallo!
Ich möchte in Lazarus genau 3 Integer Variablen (zwar mehrere male aber das ist hier egal) an den Arduino senden, dieser soll sich zurückmelden wenn alles empfangen wurde.

Wie kann ich das auf einfachstem Weg anstellen?
ich würde nicht sagen, dass ich Anfänger bin, aber gut kann ich auch nicht programmieren.
Ich finde bisher nur Möglichkeiten mit Synapse, die wirken aber auf mich extrem kompliziert.
Vielleicht hat ja jemand von euch auch schon ein ähnliches Projekt gehabt und könnte seinen Code für Lazarus und Arduino bereitstellen.
Vielen Dank!

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Mathias »

Ich finde bisher nur Möglichkeiten mit Synapse, die wirken aber auf mich extrem kompliziert.

Du kannst auch die Unit Serial verwenden, bietet aber nicht so viel Komfort wie synaser.

Ich möchte in Lazarus genau 3 Integer Variablen (zwar mehrere male aber das ist hier egal) an den Arduino senden,

Da ist Vorsicht geboten, beim Ardunio (Uno) sind die Integer 16Bit breit und bei Lazarus 32Bit.

Das sieht dann etwa so aus, wobei du Write und Read in eine Schleife nehmen kannst.
Als Rückmeldung wird hier vom Arduino ein Byte(Char) geschickt.

Code: Alles auswählen

var
  Puf: array[0..2] of Int16;
  io: byte;
begin
  serHandle := SerOpen('/dev/ttyUSB0')// Bei Windows COMx
  SerSetParams(serHandle, 9600, 8, NoneParity, 1, [RtsCtsFlowControl]);
 
  SerWrite(serHandle, Puf, SizeOf(Puf));
  SerRead(serHandle, io, SizeOf(io));
 
  SerClose(serHandle);
end


Aber ich würde die synaser empfehlen, weil man dort Timeout verarbeiten kann.
Und komplizierter ist es auch nicht, ausser das die Package Synapse installieren muss.

Bei weiter Fragen einfach melden, auch was synaser anbelangt.

Wie sieht es Arduino-Seitig aus, läuft dort schon alles ?
Was willst du genau machen ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

niklasdiy
Beiträge: 3
Registriert: Sa 4. Mär 2017, 16:24

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von niklasdiy »

Ich würde es gerne ohne Synaser probieren.
Der Arduino stellt eine Art Plotter da. Er erhält die Koordinaten (ersten beiden Zahlen) und ob ein Stift auf oder abgesetzt ist (3. Zahl). Diese Koordinate fährt er dann, über Schrittmotoren eines alten CD-Laufwerkes, an und hat dabei entweder den Stift auf dem Papier oder nicht (durch einen Servo).

Ich habe jetzt einfach probiert mit dem Code eine LED einzuschalten. Schon das funktioniert aber nicht..
Lazarus:

Code: Alles auswählen

 
var
   puf:int16;
  serHandle:TserialHandle;
begin
  puf:=1;
  serHandle := SerOpen('COM5');
  SerSetParams(serHandle, 9600, 8, NoneParity, 1, [RtsCtsFlowControl]);
  SerWrite(serHandle, Puf, SizeOf(Puf));
  SerClose(serHandle);
end;


Arduino:

Code: Alles auswählen

 
int data;
void setup()
{
  Serial.begin(9600);
  pinMode(2, OUTPUT);
}
void loop()
{
  if(Serial.available())
 {
  data = Serial.read();;
  if (data=1)
  {
   digitalWrite(2, HIGH);
  }
  }
}
 

Was mache ich falsch?
Und wie muss ich am Arduino die Daten auslesen, wenn wie du vorgeschlagen hast ein Array gesendet wird?

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von mschnell »

Unabhängig von der Art, wie man die Byte- Kommunikation macht muss man bei solchen Sachen sehr aufpassen.

- Der Empfänger muss wissen, wo die Information in einem Strom von Bytes überhaupt anfängt
- Sender und Empfänger können unterschiedliche binäre Codierungen für die Werte haben.

Also solle man die Datenblöcke Maschinen-unabhängig transparentisieren.

Ein simples Standard-Verfahren ist einen Datenblock mit einem Byte ETX zu beginnen und die werte als mit Komma getrennte lesbare Zahlen zu kodieren und am Ende am besten noch ein ETX-Byte anzuhängen geht in Lazarus alles mit einem einzigen "Format" Aufruf, da hier glücklicherweise die Standard-Strings als ASCII-kompatibles UTF-8 verwendet werden und nicht wie bei Delphi mit UTF-16).

Dann kann nichts unerwartetes passieren.
-Michael

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Mathias »

Im sketch hast du mehrere Fehler, ich werde das am Abend genauer angucken.
Lazarusseitig ist alles ok.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Mathias »

Lazarus-Seitig, habe ich folgenden Code.

Code: Alles auswählen

var
  serHandle: integer;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  Buffer: array[0..2] of Int16;
  io: byte;
begin
  Buffer[0]:=Random($100);
  Buffer[1]:=Random($100);
  Buffer[2]:=$00;
 
  SerWrite(serHandle, Buffer, SizeOf(Buffer));
  Sleep(100);
  SerRead(serHandle, io, SizeOf(io));
 
  Caption:=IntToStr(io)
end;
 
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  SerClose(serHandle);
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  serHandle := SerOpen('/dev/ttyUSB0');
  SerSetParams(serHandle, 9600, 8, NoneParity, 1, [RtsCtsFlowControl]);
end;


Beim Arduino sieht dies so aus:

Code: Alles auswählen

#include "Arduino.h"
#define PChar(pointer) reinterpret_cast<char*>(&pointer)
 
void setup()
{
   Serial.begin(9600);
 
   DDRL=0x00;
   DDRA=0xFF;
   DDRC=0xFF;
}
 
int buffer[3];
 
void loop()
{
   if (Serial.available() >= 6) {
      Serial.readBytes(PChar(buffer), 6);
 
      PORTC=(char) buffer[0];
      PORTA=(char) buffer[1];
 
      Serial.write(PINL);
   }
}

Mein Ardunio ist ein Mega, daher habe ich auch einen PORTL.
Beim einlesen wird zuerst geprüft, ob mindestens 6 Byte am COM-Port anliegen, dies entspricht deinen 3 Integer.
Dann lese ich die 6 Bytes aus dem Puffer, dafür habe ich im #define eine Hilfs-Funktion gemacht.
Ausgeben tue ich dann nur ein Byte.

Die Port und Pin, kannst du auch durch digitalWrite und digitalRead ersetzen.

Ich hoffe, du verstehst den Code.
Sonst einfach wider fragen.

Code: Alles auswählen

 if (data=1)

Dies geht leider nicht bei C++, du musst

Code: Alles auswählen

 if (data==1)

schreiben.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Timm Thaler »

Mathias hat geschrieben:Beim einlesen wird zuerst geprüft, ob mindestens 6 Byte am COM-Port anliegen, dies entspricht deinen 3 Integer.


Mit Verlaub: Das ist Schrott.

Beim Anstecken eines seriellen Kabels wird gern mal ein Startbit erkannt, dann wird ein 0x00 gelesen und steht im Buffer. Wird während der Kommunikation das Kabel gezogen, stehen Reste im Buffer. Wird während der Kommunikation das Kabel gesteckt, stehen zu wenige Byte im Buffer.

1. Mach ein vernünftiges Framing, mit einem definierten Startcode, einem definierten Endcode.
2. Mach ein vernünftiges Datenformat. Binärzahlen schicken ist ganz blöd, 0x00 sorgt oft für Ärger, Du kannst kein Framing machen...
a) entweder als Hexzahl, die Zerlegung ist total simpel, schnell, Du schickst nur 0..9 und A..F, hast also eine Menge Bytes für Steuerzeichen, Framing.
b) oder als Dezimalzahl, die Zerlegung ist auch auf einem Arduino kein Problem, das Zusammensetzen noch viel weniger.
=> Du brauchst ein paar Bytes mehr, das fällt aber meistens nicht ins Gewicht. Du sparst Dir eine Menge Ärger.
3. Warte nicht auf die Daten. Wird das Kabel gezogen, werden die Bytes nicht erkannt, stimmt das Framing nicht, hängt Dein Programm an der Stelle.
- Lies die Daten, wenn welche erkannt werden per Interrupt in einen Ringbuffer. Der Interrupt macht nichts anders, nur empfangene Daten in einen Ringbuffer schreiben.
- Im Mainloop: Sind Daten vorhanden, prüfe auf Startcode.
- Ist Startcode erkannt, wandle folgende Bytes in Deine Werte.
- Wird Endcode erkannt, prüfe Werte auf Plausibilität und übernimm sie. Sende Bestätigung.

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von pluto »

ich mache es einfach per "char*" nicht gerade Effizient aber es geht.
Am Anhang findest du ein eine Arduino Code, den musst du natürlich anpassen für deine zwecke und im Anhang findest du ein ein Unit, die mit Synapse arbeitet um den Arduino abzufragen bzw. Daten zu Senden.

Jedoch gibt es hin und wieder Probleme, ich glaube aber inzwischen, dass nicht dieser Teil von meiner "Umgebung" das Problem ist.

Aber wir können ja mal Ideen Sammeln, wie man eine gute Kommunikation zwischen Arduino und Laazarus aufbaut.
uplthreadtimertest.pas
(2.63 KiB) 93-mal heruntergeladen

uplarduinopcinterface.pas
(7.65 KiB) 103-mal heruntergeladen

Nokia_5110_LCD_27.txt
Leider sind Ino Dateien nicht erlaubt, darum einfach umbennen.
(6.25 KiB) 78-mal heruntergeladen
MFG
Michael Springwald

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Mathias »

Mit Verlaub: Das ist Schrott.

Wen man den Arduino aufs Maximum ausreizen will, ist dies überhaupt kein Schrott.

Beim Anstecken eines seriellen Kabels wird gern mal ein Startbit erkannt, dann wird ein 0x00 gelesen und steht im Buffer. Wird während der Kommunikation das Kabel gezogen, stehen Reste im Buffer. Wird während der Kommunikation das Kabel gesteckt, stehen zu wenige Byte im Buffer.

Sowas machen nur Leute, die nicht wissen was sie machen. Du zieht auch nicht einfach einen USB-Stick raus, wen dort noch geschrieben wird.

3. Warte nicht auf die Daten. Wird das Kabel gezogen, werden die Bytes nicht erkannt, stimmt das Framing nicht, hängt Dein Programm an der Stelle.

Das ist ein Grund, synaser anstelle von Serial zu verwenden. Bei synaser kann man ein TimeOut eingeben und eine Fehlermeldung bringen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von pluto »

3. Warte nicht auf die Daten. Wird das Kabel gezogen, werden die Bytes nicht erkannt, stimmt das Framing nicht, hängt Dein Programm an der Stelle.

Aus den Grund habe ich z.b. ein Zähler in der Schleife eingebaut. Wird die schleife eine bestimmte Anzahl durchgelaufen, bricht die Schleife ab.

Ich mache es wie folgt:
Mein Lazarus Programm fragt alle 5 Minuten die Arduino's ab. Der reihe nach. Der Arduino Antwortet und hat eine bestimmte Zeit dafür. Die Zeit ist aber sehr großzügig gewählt.
Das mache ich, damit sich das Programm bzw. der Thread nicht aufhängt.

Ich frage mich, wie sieht wohl eine "Perfekte" Kommunikation aus?
Reste im Buffer kann man löschen beim Öffnen der Schnittstelle. Per Synapse. Außerdem wird unter Linux Automatisch ein Reset vom Arduino durchgeführt.

Beim Verbinden. Es gibt Möglichkeiten diesen Reset zu unterbinden.... genau weiß ich es nicht mehr, aber es geht nur mit weiteren Komponenten auf der Arduino Seite. So konnte ich das reset z.b. von einem Arduino Mega unterbinden, weil es zu den Zeitpunkt gestört hat. Dazu habe ich ein Jumper auf der Platine gelötet, damit es einstellbar ist.

Das gute ist, bei Synapse man braucht für die eigentliche Serielle Anwendung, kaum Units von Synapse.
MFG
Michael Springwald

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Mathias »

Aus den Grund habe ich z.b. ein Zähler in der Schleife eingebaut. Wird die schleife eine bestimmte Anzahl durchgelaufen, bricht die Schleife ab.

Intern läuft das bei synaser auch so.

Ich frage mich, wie sieht wohl eine "Perfekte" Kommunikation aus?

Ich denke mal, der COM-Port ist nicht die ideale Kommunikation. Evtl. könnte man beim Arduino Leonard über HID kommunizieren.
Ich hatte kürzlich mit dem PC über I²C auf EEPROM geschrieben.
Da schreibt man fortlaufend in den Baustein, kommt aber anschiessend ein zweites fpWrite, merkt der irgendwie, das der nächste Datensatz kommt.

Es gibt Möglichkeiten diesen Reset zu unterbinden.... genau weiß ich es nicht mehr,

Das hat etwas mit RTS zu tun.

Das gute ist, bei Synapse man braucht für die eigentliche Serielle Anwendung, kaum Units von Synapse.

Ich habe trotzdem die ganze Pakage installiert. Man könnte sicher noch optimieren mit ausmisten. Auch in synaser selbst hat es vieles das nicht gebraucht wird.

Zum empfangen würden RecvBufferEx reichen, der Rest leitet fast alles davon ab. Bei Senden sieht es ähnlich aus,
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von pluto »

Ich denke mal, der COM-Port ist nicht die ideale Kommunikation. Evtl. könnte man beim Arduino Leonard über HID kommunizieren.
Ich hatte kürzlich mit dem PC über I²C auf EEPROM geschrieben.

Bei I²C stört mich das pollen. der Master muss ja ständig den Slave abfragen, ob sich was geändert hat.
MFG
Michael Springwald

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Timm Thaler »

Mathias hat geschrieben:Wen man den Arduino aufs Maximum ausreizen will, ist dies überhaupt kein Schrott.


Wenn man einen Atmega aufs Maximum ausreizen will, macht man das gewiss nicht mit der Arduino IDE. Ich hab Atmegas schon in ASM programmiert, da war Arduin noch ein italienischer König. Und ja, ich habe die Dos und Don'ts im Umgang mit der Seriellen auch auf die harte Tour lernen müssen.

Sowas machen nur Leute, die nicht wissen was sie machen.


Meine Geräte müssen feldtauglich sein. Da kann nicht der µC stehenbleiben, weil einer übers Kabel stolpert.

Das ist ein Grund, synaser anstelle von Serial zu verwenden. Bei synaser kann man ein TimeOut eingeben und eine Fehlermeldung bringen.


SerReadTimeout(uhnd, brx, 10) aus serial.pp

Nur wartet meine Empfangsroutine max. 10msec und macht weiter, wenn keine Daten da sind. Wie lange blockierst Du Dein Programm so?

pluto hat geschrieben:Bei I²C stört mich das pollen. der Master muss ja ständig den Slave abfragen, ob sich was geändert hat.


Sowohl der Atmega als auch der Raspi können ja I2C hardwaremäßig. Allerdings macht einen I2C Slave bauen nicht wirklich Spass, glaub mir. Ich würd bei der Seriellen bleiben. Oder wenn es richtig schnell sein muss SPI, und dann über die Hardware. Allerdings bringt der Atmega 328, auf dem der normale Arduino beruht nicht annähernd genug Ram mit, um hohe Datenraten zu benötigen.

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von Mathias »

Wenn man einen Atmega aufs Maximum ausreizen will, macht man das gewiss nicht mit der Arduino IDE. Ich hab Atmegas schon in ASM programmiert, da war Arduin noch ein italienischer König.

Wieso sollte man nicht die Arduino-IDE nehmen ?
Dort kann man bei Zeitkritischen Sachen auch Asm-Blöcke einfügen. Nur reines Asm habe ich nicht hingekriegt, es scheiterte schon daran, das irgendwo im Hintergrund setup und loop aufgerufen wird.
Was bei Arduino ein riesen Gift ist, ist digitalWrite und digitalRead, die trozen richtig mit Overhead. Nur schon mit einem DDRx, PINx und PORTx holt man schon sehr viel raus.

Meine Geräte müssen feldtauglich sein. Da kann nicht der µC stehenbleiben, weil einer übers Kabel stolpert.

Nur wen bei meiner Anwendung dies der Fall ist, dann wird auch der Strom zu Arduino unterbrochen und beim nächsten anschliessen gibt es ein Reset.

Allerdings macht einen I2C Slave bauen nicht wirklich Spass, glaub mir.

Dies habe ich mal für eine Laufschrift gemacht, ich fand das nicht so tragisch,

ich habe die Dos und Don'ts im Umgang mit der Seriellen auch auf die harte Tour lernen müssen.

Das waren noch Zeiten mit $3F8. :mrgreen:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

niklasdiy
Beiträge: 3
Registriert: Sa 4. Mär 2017, 16:24

Re: Kommunikation zwischen Arduino und Lazarus

Beitrag von niklasdiy »

Schönen Guten Tag!
Ich melde mich zwar ein wenig spät zurück, aber besser so als nie. :mrgreen:
Erst einmal vielen Dank für eure Antworten.
Der Quelltext von Mathias gefällt mir gut, da er für meine simple Anwendung genau tut was er soll. Ich brauche vorerst keine Start und Endbefehle.
Ich habe dennoch ein paar Fragen dazu.
Die Kommunikation zwischen Lazarus und meinem Plotter ist aufgebaut. Er tut aber nicht was er soll.
Ich sende meine Koordinaten PointerX und PointerY (beide integer zwischen 0 und 1000) und zusätzlich eine z-Koordinate (zwischen 0 und 1).

Code: Alles auswählen

 
 
var   Buffer: array[0..2] of Int16;
begin
 
  Buffer[0]:=pointerx;
  Buffer[1]:=pointery;
  Buffer[2]:=zstate;
  SerWrite(serHandle, Buffer, SizeOf(Buffer));
 
end;
 



Der Drucker verarbeitet die Daten so:

Code: Alles auswählen

 
#define PChar(pointer) reinterpret_cast<char*>(&pointer)
int puffer[3];
 
void loop() {
if (Serial.available() >= 6) {
      Serial.readBytes(PChar(puffer), 6);
 
      if (puffer[2]==0) {
        if (zwert == 1){
        myservo.write(0); // überträgt die Zielposition an den Servomotors
        delay(500);
        }
      }
 
       if (puffer[2]==1) {
        if (zwert == 0){
        myservo.write(90); // überträgt die Zielposition an den Servomotors
        delay(500);
        }
      }
      koordinate(puffer[0],puffer[1]);
   }
}
 



koordinate() fährt in dem Fall die X und y Koordinate an, davor wird entschieden ob der Servo den Stift aufnimmt oder absetzt

Der Drucker macht auch irgendwas, nur nicht das was er soll. Er fährt wild Koordinaten an, die sind aber nicht dieselben, die ich ihm sende.
Meine Vermutung ist, dass ja nicht die Variable PointerX sondern nur die Anzahl deren Bytes gesendet wird.

(1) Wie kann ich das lösen?

(2)Könnte mir jemand genau er klären was in

Code: Alles auswählen

 
SerSetParams(Handle: TSerialHandle; BitsPerSec: LongInt;
  ByteSize: Integer; Parity: TParityType; StopBits: Integer;
  Flags: TSerialFlags);
 

deklariert wird?

Antworten