Ich habe folgendes Problem.
Ich möchte gerne meinen DHT22 an einem Mega auslesen, was auch funktioniert. Die Werte sind plausibel.
Dann möchte ich diese gerne an einen UNO seriell senden, da sie dort im Fortgang des Projektes weiterverarbeitet werden sollen.
Momentan arbeite ich mich durch die Grundlagen von C und habe jedoch mit dem Thema Zeichenketten Probleme.
Mir ist klar dass ich am Sender ein Array mit den beiden Variablen mit der Länge 2 erzeugen muss.
Dies schicke ich dann über den HardwareSerial Port 2 (der Mega hat das) seriell an den UNO.
Auf der Empfängerseite müsste doch dann das Empfangene Array wieder zerlegt werden oder?
Jedenfalls habe ich das so gemacht und ich empfange nur willkürliche Zahlen.
Kann sich das bitte jemand ansehen.
Haut aber bitte nicht direkt druff wenn es trivial ist, bin mit den Zeichenketten noch nicht so weit >:(
#include "DHT.h" //DHT Bibliothek einbinden
#define DHTPIN 2 //DHT22 auf Pin 2
#define DHTTYPE DHT22 //Typ DHT22
DHT dht(DHTPIN, DHTTYPE);
int h_as_int; //Werte sollen als INT übertragen werden und später am Empfänger zur Dezimalzahl gemacht
int t_as_int;
int Messwerte[2]={ h_as_int, t_as_int};// Array der Länge 2 erzeugen und mit den beiden Variablen füllen
void setup() {
Serial.begin(9600); //Start des seriellen Monitors
Serial2.begin(9600); //Start der Hardware Seriellen Schnittstelle 2
dht.begin(); //Start des Sensors DHT22
delay(3000); // "3 Sekunden warten bis Sensor hochgefahren ist"
}
void loop() {
float h = dht.readHumidity(); //Feuchte auslesen
float t = dht.readTemperature(); //Temperatur auslesen
int h_as_int = round(h); // ordentlich runden und nicht abschneiden, als INT speichern
int t_as_int = round(t * 10); // Komma verschieben und runden
Serial.print("Luftfeuchte");
Serial.print(h_as_int);
Serial.println("Temperatur");
Serial.print(t_as_int);
Serial2.print(Messwerte[2]); //Messwerte über serielle Schnittstelle schicken
delay(5000); //fünf Sekunden warten
}
Empfänger
int laenge=2;
char empfangeneDaten[2]; //Array der Länge 2
void setup(){
Serial.begin(9600);}//Start der seriellen Schnittstelle
void loop() {
int i=0;
if ( Serial.available()) //Wenn serielle Daten vorhanden...
{
empfangeneDaten[i++] = Serial.read();
delay(2);
Serial.print(char(empfangeneDaten[0])); //Wert1 Feuchte
Serial.print(char(empfangeneDaten[1])); //Wert 2 Temp
}
ClearArray(); //Array wieder freimachen für den nächsten Datensatz
}
void ClearArray()
{
for (int x=0; x<laenge; x++)
{
empfangeneDaten[x]=NULL;
}
}
Da sind mehrere Haken und Ösen drin
Der UNO hat nur eine HW-serielle, und die wird bereits für die USB Schnittstelle genutzt. Du musst also für die Verbindung zum Mega SoftwareSerial verwenden.
Das
int Messwerte[2]={ h_as_int, t_as_int};// Array der Länge 2 erzeugen und mit den beiden Variablen füllen
funktioniert nicht. Du musst die Werte im loop explizit eintragen ( könntest dann aber auch gleich deine beide int-Werte versenden )
Auch das
Serial2.print(Messwerte[2]); //Messwerte über serielle Schnittstelle schicken
macht nicht dass was Du willst. Du sendest hier einen Integer, der hinter deinem Array steht - also was vollkommen undefiniertes. Bei einem Array der Länge 2 gibt es nur die Inidizes 0 und 1. Und die musst Du einzeln verschicken.
Beim Empfänger kommt die Zahl als ASCII-String an. Damit Du das Ende erkennen kannst solltest Du sie deshalb als println schicken, sonst ist das nur eine Ziffernwurst. Und dann musst Du beim Empfanger dementsprechend auch auf das Zeilenende abfragen. Um Temperatur und Feuchte unterscheiden zu können, empfiehlt sich gegebenenfalls noch eine entsprechende Startkennung, z.B. 'T' imd 'F'
Und hier: char empfangeneDaten[2]; //Array der Länge 2brauchst Du ein char-Array, das die komplette empfangene Zeile aufnehmen kann. Und die musst Du dann auch noch entsprechend interpretieren ( Kennung für Feuchte/Temperatur auswerten, und Ziffern wieder in Integer wandeln ( mit atoi() ),
Es geht also nicht ganz so einfach, wie Du dir das vorgestellt hast
vielen Dank schonmal. Ich werde mich jetzt da durcharbeiten. Wenn ich soweit bin werde ich den Code überarbeiten.
Das heißt dann also ich erstelle am Sender gar kein Array und sende beide INT - Werte über die Funktion Serial2.println hintereinander.
Mit Software Serial am UNO zu arbeiten ist auch sinnvoll. Sonst kollidieren die Datensätze mit dem USB - Port. Es ist nicht so einfach :o
Entscheide dich dafür was du übertragen willst. Du redest was von Zeichenketten, aber dann behandelst du das auf dem Empfänger nicht so. Dazu muss bis zu einem Endzeichen (meistens ein Linefeed einlesen). Und man muss mit NULL terminieren.
Wozu dann auch das Array groß genug sein muss. Ein char Array der Länge 2 hat eigentlich nur Platz für ein Zeichen
Einen Buchstaben voranstellen. Auf den kann man abfragen. Und dann bei der Konvertierung +1 machen damit die Funktion mit dem zweiten Buchstaben anfängt
ich kann einen Erfolg verzeichnen. Die Werte kommen sauber an. Im Buch von Herrn Brühlmann ist ein gutes Beispiel.
Jetzt habe ich nur noch ein Problem: Wie bekomme ich den String so zerlegt dass ich wie im vorherigen Beitrag mit der Switch Case Bedingung die Werte in Temp und Hum unterscheiden kann?
Im seriellen Monitor des Empfängers sehe ich jetzt F50 ; T223 Das entspricht 50%Feuchte und 22,3°C
Die beiden Werte möchte ich jetzt in zwei Variablen haben
#include "DHT.h" //DHT Bibliothek einbinden
#define DHTPIN 2 //DHT22 auf Pin 2
#define DHTTYPE DHT22 //Typ DHT22
DHT dht(DHTPIN, DHTTYPE);
int h_as_int; //Werte sollen als INT übertragen werden und später am Empfänger zur Dezimalzahl gemacht
int t_as_int;
void setup() {
Serial.begin(9600); //Start des seriellen Monitors
Serial2.begin(9600); //Start der Hardware Seriellen Schnittstelle 2
dht.begin(); //Start des Sensors DHT22
delay(3000); // "3 Sekunden warten bis Sensor hochgefahren ist"
}
void loop() {
float h = dht.readHumidity(); //Feuchte auslesen
float t = dht.readTemperature(); //Temperatur auslesen
int h_as_int = round(h); // ordentlich runden und nicht abschneiden, als INT speichern
int t_as_int = round(t * 10); // Komma verschieben und runden
Serial.print("Luftfeuchte");
Serial.print(h_as_int);
Serial.println("Temperatur");
Serial.print(t_as_int);
Serial2.print("F");
Serial2.print(h_as_int);
Serial2.print(" ; ");
Serial2.print("T");
Serial2.println(t_as_int);
//Messwerte über serielle Schnittstelle schicken
delay(5000); //fünf Sekunden warten
}
#include <SoftwareSerial.h> // Software Serial Bibliothek einbinden
SoftwareSerial mySerial(2, 3); //PIN2 ist RX, PIN 3 ist TX
char empfangeneDaten[255];
int laenge=0;
void setup()
{
Serial.begin(9600); //Start der seriellen Schnittstelle, an der der USB angeschlossen ist
mySerial.begin(9600); //Start der seriellen Schnittstelle, an der Datensätze empfangen werden sollen
}
void loop() {
int i=0;
// Wenn serielle Daten vorhanden sind
if (mySerial.available() > 0) {
while (mySerial.available()) {
empfangeneDaten[i++] = mySerial.read();
delay(2);
}
Serial.print(empfangeneDaten);
}
ClearArray();
}
void ClearArray()
{
for (int x=0; x<laenge; x++)
{
empfangeneDaten[x] = NULL;
}
}
Du liest die Zeichen einzeln ein.
Wenn Du ein F erkennst, leerst Du die variable für die Feuchte - Alles, was ab nun kommt, landet da drin.
Erkennst Du ein T, Gleiches mit der Temperatur.
Ok, Alles war gelogen - wir prüfen auf >='0' && <='9' - wenn also ein Zahlzeichen übergeben wurde, dann
alter_Wert*=10; //alten Wert x 10
alter_Wert+=Zeichen; //Das Zeichen addieren
alter_Wert-=0x30; //'0' -> 0x30, wenn wir '0' addieren und 0x30 abziehen, haben wir +0 gerechnet
... klappt auch bei den Zahlziffern 1-9, nur bleibt Da halt 'mehr' über.
Als Trennzeichen verwendest Du ; bzw. das Zeilenende CR/LF.
Wenn ein Trennzeichen gefunden wurde, wird mit der Erkennung/Erweiterung des Wert aufgehört.
Den Null-Terminator unterschlägst du immer noch...So wird das nicht. Außerdem überprüfst du nicht ob du über Array Grenzen schreibst (Bei 255 Byte wird das nicht passieren, aber man muss nicht gleich übertreiben). Und bei höheren Baudraten bekommst du so auch Probleme.
In dem Link von mir ist eine fertige Einlese-Funktion die das alles berücksichtigt. Die fragt immer wieder ob was im Eingangspuffer ist. Wenn ja wird das Zeichen eingelesen und NULL zurückgegeben. Wenn ein LF eingelesen wurde bekommt man einen Zeiger auf den Puffer und kann was damit machen.
Das geht alles nicht-blockierend. Man kann dann dazwischen andere Sachen machen aber die Funktion muss ständig in loop() aufgerufen werden. Nicht nur einmal.
Wenn du ein 'F' oder 'T' voranstellst kannst du zwei getrennte Zeilen senden. println() hängt CR + LF an. Auf das LF kann man dann abfragen und weiß wenn eine Zeile fertig ist. Wie man dann von da auch die Werte kommt steht in Beitrag #4
Alternativ kann man auch beides in einer Zeile senden (ohne Buchstaben) und mit strtrok() trennen. Aber entscheide dich für eine Variante und vermische das nicht wie du es oben gemacht hast!
So, so langsam ist das Ziel in Sicht. Habs jetzt mit der zweiten Variante gemacht. Beide Werte ohne Buchstaben in eine Zeile geschrieben und mit Semikolon getrennt. Diese kommen dann auch so an.
Den komischen Nullterminator brauche ich dann wohl auch nicht.
Wenn ich die zerlegten Strings auf dem seriellen Monitor ausgebe zeigt dieser mir dafür aber null an. Hat jmd. eine Ahnung wo dran das liegt. Kann ja nicht mehr viel sein
Ich habe mich in die Funktion strtok() eingelesen und "denke" das ist wohl so richtig. Über die Atoi Befehle hole ich mir den Integer des Wertes. Aber warum kommt immer null?
#include "DHT.h" //DHT Bibliothek einbinden
#define DHTPIN 2 //DHT22 auf Pin 2
#define DHTTYPE DHT22 //Typ DHT22
DHT dht(DHTPIN, DHTTYPE);
int h_as_int; //Werte sollen als INT übertragen werden und später am Empfänger zur Dezimalzahl gemacht
int t_as_int;
void setup() {
Serial.begin(9600); //Start des seriellen Monitors
Serial2.begin(9600); //Start der Hardware Seriellen Schnittstelle 2
dht.begin(); //Start des Sensors DHT22
delay(3000); // "3 Sekunden warten bis Sensor hochgefahren ist"
}
void loop() {
float h = dht.readHumidity(); //Feuchte auslesen
float t = dht.readTemperature(); //Temperatur auslesen
int h_as_int = round(h); // ordentlich runden und nicht abschneiden, als INT speichern
int t_as_int = round(t * 10); // Komma verschieben und runden
Serial.print("Luftfeuchte");
Serial.print(h_as_int);
Serial.println("Temperatur");
Serial.print(t_as_int);
Serial2.print(h_as_int);
Serial2.print(";");
Serial2.println(t_as_int);
//Messwerte über serielle Schnittstelle schicken
delay(5000); //fünf Sekunden warten
}
#include <SoftwareSerial.h> // Software Serial Bibliothek einbinden
SoftwareSerial mySerial(2, 3); //PIN2 ist RX, PIN 3 ist TX
char empfangeneDaten[255];
int laenge=0;
char str[]=";";//Trennzeichen sind Semikolon
int value1;//Luftfeuchte
int value2;//Lufttemperatur
void setup()
{
Serial.begin(9600); //Start der seriellen Schnittstelle, an der der USB angeschlossen ist
mySerial.begin(9600); //Start der seriellen Schnittstelle, an der Datensätze empfangen werden sollen
}
void loop() {
int i=0;
// Wenn serielle Daten vorhanden sind
if (mySerial.available() > 0) {
while (mySerial.available()) {
empfangeneDaten[i++] = mySerial.read();//Daten von der seriellen Schnittstelle einlesen
int value1 = atoi(strtok(str, ";"));//Wert 1 Feuchte
int value2 = atoi(strtok(NULL, ";"));//Wert 2 Temperatur
delay(2);
}
Serial.println(empfangeneDaten);
Serial.print(value1);
Serial.println(value2);
}
ClearArray();
}
void ClearArray()
{
for (int x=0; x<laenge; x++)
{
empfangeneDaten[x] = NULL;
}
}
Dein Arduino ist ein startendes Flugzeug.
Deine seriellen Daten sind die Rentner am Rollator.
Nun überlege, wie viele Rentner Du mit Deinem Flugzeug 'deuten' kannst, bevor Du weit weit weg bist.
Du musst SAMMELN, bis die Daten alle da sind - und DANN ERST auswerten.
Momentan prüfst Du, 'ob Was Da ist'.
Egal Was ... Hauptsache: Es ist Was da.
Dann versuchst Du, Dieses 'egal Was' in zwei Happen einzulesen - ob hier überhaupt schon der Erste überhaupt angefangen hat ... ich möchte Behaupten: Nö
Dann gibst Du die soeben erlangten (gar nicht vorhandenen) Daten aus.
Dein 'Glück' besteht noch darin, daß atoi wohl Null zurück gibt, wenn Nix kam - sonst hättest Du irgend welche Hausnummern, da der Speicherplatz, wo Du Deine INTs anlegst, zuvor undefiniert war.
Also: Gerne zügig, aber auf manches muß man auch warten können (Warten nun aber nicht mit delay() gleich setzen)
Dein ganzes Prinzip ist Murks. Du brauchst ein Endzeichen! Das hast du mit dem Linefeed von println(). Im Moment geht das durch das delay() nach jedem Zeichen, aber schön ist das nicht. Bei dieser Anwendung wird es zwar keine Rolle spielen, aber wenn man sonst noch was machen will stört das
Den komischen Nullterminator brauche ich dann wohl auch nicht.
Falsch! Bitte verlasse sich dich nicht darauf dass Code zufällig das macht was du erwartest wenn du nicht verstehst was da eigentlich abläuft. atoi() geht in bestimmten Situationen auch ohne Terminator, da es abbricht wenn es auf etwas trifft das keine Ziffer ist. Die meisten anderen String Funktionen wie strtok() oder auch println() erwarten einen richtig terminierten String. Wie sollen die sonst merken dass sie Abbrechen sollen?
Wenn man das richtig macht braucht man auch kein clearArray(). Dadurch dass du ganze Array immer auf 0 setzt hat du eine Terminierung drin. Es reicht aber wenn man einfach das letzte Zeichen auf 0 setzt.
Und wenn man wirklich ein ganzes Array beschreiben will nimmt man normal memset()
Dann kannst du nicht ein Zeichen einlesen und sofort Auswerten. Das ist der Hauptfehler. Du musst bis zum Ende einlesen. Und da kommt wieder das Linefeed in Spiel
Es geht auch mit anderen Varianten aber dann hat man Dinge wie Blockierungen und Timeouts drin. Und deren minimal nötige Zeiten hängen von der Baudrate ab.
Da kommt wieder null. Ich glaube aber dass es wohl eine Hausnummer zu groß ist und so kein Sinn ergibt.
Ist zwar schade, wollte mir eine Wetterstation aufbauen und habe es schon geschafft mit dem Arduino über Modbus Profisensoren auszulesen aber hier klappts irgedwie nicht.
#include <SoftwareSerial.h> // Software Serial Bibliothek einbinden
SoftwareSerial mySerial(2, 3); //PIN2 ist RX, PIN 3 ist TX
const unsigned int READ_BUFFER_SIZE = 17; //16 Zeichen + Terminator
char* txt ;
int text;
void setup()
{
Serial.begin(9600); //Start der seriellen Schnittstelle, an der der USB angeschlossen ist
mySerial.begin(9600); //Start der seriellen Schnittstelle, an der Datensätze empfangen werden sollen
}
void loop()
{
const unsigned int READ_BUFFER_SIZE = 17; //16 Zeichen + Terminator
char* txt = readLine(mySerial);
if (txt != nullptr)
{
Serial.print("Empfangen: ");
Serial.println(text);
}
}
char* readLine(Stream& stream)
{
static byte index;
static char buffer[READ_BUFFER_SIZE];
while (stream.available())
{
char c = stream.read();
if (c == '\n') //wenn LF eingelesen
{
buffer[index] = '\0'; //String terminieren
index = 0;
return buffer; //melden dass String fertig eingelesen wurde
}
else if (c >= 32 && index < READ_BUFFER_SIZE - 1) //solange noch Platz im Puffer ist
{
buffer[index++] = c; //Zeichen abspeichern und Index inkrementieren
}
}
return nullptr; //noch nicht fertig
}
READ_BUFFER_SIZE ist eine globale Variable. Das nochmal lokal zu machen bringt nichts. txt reicht als lokale Variable
Sendest du beim Sender ein Linefeed am Ende? Auf print() reagiert das nicht. Da muss println() stehen (das sendet CR + LF und das CR wird ignoriert).
Den Unterschied merkst du auch wenn du das einfach mal mit Serial und dem seriellen Monitor ausprobierst