ich habe mich heute wieder etwas mit dem Arduino und TCP beschäftigt.
Vorweg, der Code unten ist großteils aus Beispielen zusammenkopiert.
Er läuft, aber ich möchte ihn erweitern und besser verstehen...
Ich habe das mit dem Komentaren noch nicht geradegezogen.
Also nicht wundern das da kein DHCP mehr läuft, das ist so mein aktueller Stand vom Testaufbau hier.
Ich weiß das hier ein FLOAT in 4 Bytes zerlegt wird die dann über TCP rausgehen.
Aber könnt ihr mir die die Syntax bitte genauer erklären?
Danke!
Wie kann ich es realisieren, dass ich über die nächsten 4 Byte die Feuchtigkeit sende?
Dann würde ich gerne noch einen Integer mitschicken den ich Zyklisch erhöhe (Hartbeat um die Kommunikation beim Empfänger zu überwachen)
Vermutlich wird es schicker erst alle Daten in ein ArrayOfByte zu legen und dann mit client.write() alles auf einmal rauszuschicken. oder kann ich in C so eine Art Structur deklarieren (bestehens aus FLOAT;FLOAT;INT), diese beschreiben und dann an client.write() zu übergeben?
/*
* UIPEthernet TcpClient example.
*
* UIPEthernet is a TCP/IP stack that can be used with a enc28j60 based
* Ethernet-shield.
*
* UIPEthernet uses the fine uIP stack by Adam Dunkels <adam@sics.se>
*
* -----------------
*
* This TcpClient example gets its local ip-address via dhcp and sets
* up a tcp socket-connection to 192.168.0.1 port 5000 every 5 Seconds.
* After sending a message it waits for a response. After receiving the
* response the client disconnects and tries to reconnect after 5 seconds.
*
* Copyright (C) 2013 by Norbert Truchsess <norbert.truchsess@t-online.de>
*/
#include <UIPEthernet.h>
#include <DHT.h>
EthernetClient client;
signed long next;
#define DHTPIN 2 // Digital pin connected to the DHT sensor
DHT dht(DHTPIN, DHT22);
void setup() {
Serial.begin(9600);
dht.begin();
uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};
IPAddress myIP(192,168,20,71);
Ethernet.begin(mac,myIP);
Serial.print("localIP: ");
Serial.println(Ethernet.localIP());
Serial.print("subnetMask: ");
Serial.println(Ethernet.subnetMask());
Serial.print("gatewayIP: ");
Serial.println(Ethernet.gatewayIP());
Serial.print("dnsServerIP: ");
Serial.println(Ethernet.dnsServerIP());
next = 0;
}
float humidity;
float temp;
void loop() {
if (((signed long)(millis() - next)) > 0)
{
next = millis() + 5000;
Serial.println("Client connect");
// replace hostname with name of machine running tcpserver.pl
// if (client.connect("server.local",5000))
if (client.connect(IPAddress(192,168,20,11),2000))
{
Serial.println("Client connected");
humidity = dht.readHumidity();
temp = dht.readTemperature();
Serial.print("Luftfeuchtigkeit: ");
Serial.print(humidity);
Serial.println("%");
Serial.print("Temperatur: ");
Serial.print(temp);
Serial.println("°C");
// client.println("DATA from Client");
client.write((const uint8_t *) & temp, sizeof(temp));
//disconnect client
Serial.println("Client disconnect");
client.stop();
}
else
Serial.println("Client connect failed");
}
}
in der Print.cpp gibt es einen overload für write mit genau dieser Signatur die einen Buffer vereinzeilt (bzw. wenn du dir den Code ansiehst Bytes ab einem Pointer an die Schnittstelle sendet).
einfach den nächsten Wert wieder mit write senden.
einfach ein uint32_t (z.B.) senden - also noch mal 4 byte.
class DumpInHex : public Print{
public:
using Print::write;
size_t write(uint8_t c)
{
if (c <10 ) Serial.print('0');
Serial.print(c, HEX);
Serial.print(' ');
}
};
DumpInHex client;
float temp = 12.34;
float humidity = 56.78;
uint32_t counter = 123456;
void setup() {
Serial.begin(115200);
client.write((const uint8_t *) & temp, sizeof(temp));
client.write((const uint8_t *) & humidity, sizeof(humidity));
client.write((const uint8_t *) & counter, sizeof(counter));
}
void loop() {
// put your main code here, to run repeatedly:
}
ja einen eigenen Buffer könnte man machen.
PS)
Ich würde nicht über Systemgrenzen (Arduino != SPS) ein Float in binary senden. Ich würde das in plain ASCII machen und dann vermutlich einen Felddelimiter (, oder ; ) setzen.
Ich dachte nicht dass ich die Werte einfach durch erneute Aufrufe von client.print() erneut senden kann. Ich habe erwartet, dass die Funktion immer wieder bei Byte-0 beginnt und alles überschreibt.
Mit dem FLOAT hast du recht.
Was hältst du davon ihn mit *10(eine Nachkommastelle reicht) als uint32 zu schicken?
Das lässt sich auf der SPS einfacher verarbeiten.
Deinen Beispielcode habe ich noch nicht verstanden.
Wozu brauche ich die Klasse DumpinHex?
Ist das nur für diesen Onlinesimulator relevant damit er das Ergebnis unten auf der Konsole ausspuckt?
Sorry aber ich verstehe nichts von C
.print() ist was anderes als .write().
du hast mit .write() begonnen, daher habe ich mit .write() fortgesetzt.
es gibt kein "überschreiben" in dem Sinne. Das ist ein Stream. Du schickst was los und das geht auf die Reise. Der nächste write/print überschreibt nicht, er schickt einfach was neues. *)
klingt gut.
ist nur Demo damit man genau den write Aufruf ausprobieren kann und eben in HEX sieht, was da über die Schnittstelle geschickt werden würde. Den "Play" Button hast eh gesehen oder?
Ändere die client.write aufrufe mal auf Serial.write ... kannst da noch was lesen? ich vermute nicht ergo diese kleine Helper Klasse die eben die (binary) Byte Ausgabe in HEX ein wenig aufbereitet.
Ist nur fürs Auge.
Du kannst natürlich auch zwei Arduinos nehmen und eine echte TCP Verbindung aufbauen und einen "Empfänger" Sketch (Server) schreiben. Ich fand die Umleitung auf Serial so halt einfacher.
*) Anmerkung um Rosinenpicken vorzubeugen: es hängt an der Implementierung der jeweiligen Klasse. Aber sagen wir mal so, die "Standard" Sachen wie Serial, lcd, tcp, ... die schicken i.d.R jedes Byte los.
Sorry, ich meinte .write
.print schickt ASCII raus wenn ich das richtig verstanden habe.
es gibt kein "überschreiben" in dem Sinne. Das ist ein Stream. Du schickst was los und das geht auf die Reise. Der nächste write/print überschreibt nicht, er schickt einfach was neues. *)
Rein Interessehalber:
Wan wird das dann "initialisiert" bzw. wann startet die Funktion wieder mit Byte-0?
Nachdem schließen und aufbauen der Verbindung?
ist nur Demo damit man genau den write Aufruf ausprobieren kann und eben in HEX sieht, was da über die Schnittstelle geschickt werden würde. Den "Play" Button hast eh gesehen oder?
Verstehe, das habe ich schon vermutet.
Danke dir! Du hast mir sehr geholfen!
Was anderes noch:
Mein Code ist wie gesagt noch nicht so gut aufgeräumt, das mache ich heute Abend noch.
Sollte ich noch was in Punkto Fehlerhandling beachten?
Kann sich mein Wunderwerk irgendwie aufhängen bzw. irgendwo nicht mehr rauskommen wenn es z.B. einen Verbindungsfehler gibt?
Ich werde das heute Abend auch noch umbauen auf eine bidirektionale Kommunikation.
SPS sendet einen Request mit einem counter oder Zeitstempel
Nano antwortet darauf mit den Daten und gibt den cnt bzw. tmstmp zurück.
Dann kann die SPS entscheiden wie schnell sie ihre Daten haben will, und die Plausibilität besser überwachen.
Außerdem macht print je nach Datentyp z.B. aus einer Float-Zahl erstmal einen Text mit fester Anzahl Nachkommastellen ("-12.34"), um dann diese 6 Zeichen per write zu senden.
Das ist die andere wesentliche Eigenschaft von print, dass es zig Varianten davon gibt, für alle möglichen Datentypen
Danke euch mal soweit.
Ja das mit print und den Nachkommastellen habe ich schon rausgefunden.
Man kann die gewünschte Anzahl aber auch an die Funktion übergeben.
"Print.h" ist sehr mächtig.
Schau dir z.B. mal die Implementierung der PString-Library an. Die ermöglicht dir z.B. dass du in einen "Buffer" mit print "druckst".
Also wenn du den ASCII Weg gehen willst - durchaus ansehenswert.