Halli Hallo,
Ich hätte da mal eine Frage, ich habe ein Projekt indem ich mit einem Arduino Nano (Slave) zu einem Arduino Uno (Master) Daten verschicke mit 2 Bluetooth Modulen Typ HC-05.
Meine Problem ist jetzt, das ich an meinem Seriellen Monitor für den Master falsche Zahlen erhalte, obwohl ich auf dem Seriellen Monitor des Slave die richtigen Zahlen habe.
Ich habe eine Vermutung das es an der Kommunikation der zwei Bluetooth liegt, aber ich weis nicht wie ich es beheben soll.
Das ist mein Slave Code:
#include<SoftwareSerial.h>
const int rxPin = 10;
const int txPin = 11;
int x = 0;
SoftwareSerial mySerial(rxPin,txPin);//RX,TX.
}
void setup()
{
Serial.begin(9600);
mySerial.begin(9600);
}
void loop()
{
Serial.println(x);
mySerial.write(x);
Serial.print(x);
delay (1000);
x++;
}
Ich hoffe, mir kann jemand helfen und hoffe auf eine schnelle Antwort.
P.S.: Es wäre nett wenn man mehrere Lösungsvorschläge angibt, weil ich schon sehr viel selber versucht habe. Aber freue mich trotzdem auf jede Antwort!
Dein "x" ist ein int. Das sind wie viele Byte, die Du da auf die Reise schickst?
Die Dokumentation von SoftwareSerial.read() enthält die Information darüber, wie viele Byte mit einem Aufruf gelesen werden.
Es wäre nett, wenn man sich bemüht, selbst auf die Lösung zu kommen - das hilft bei zukünftigen Problemen ungemein.
Von all deinen Versuchen bleibt nur die Behauptung, etwas sei falsch.
Vermutlich ist es eher so, dass etwas auf beiden Seiten unterschiedlich ist und unterschiedlich dargestellt wird, je nach dem was du damit machst.
Z. B. macht Serial.write(x) etwas anderes als Serial.println(x), auch wenn der Wert von x gleich bleibt.
Auch Walters Hinweis auf den fragwürdigen Datentyp int bei read() und write() solltest du in deine "vielen Versuche" mit einbeziehen.
Zum Testen kannst du die Bluetooth-Verbindung übrigens durch 3 Drähte (rx--tx / tx--rx / gnd--gnd ) ersetzen. Das sollte keinen Unterschied machen. (Kann allerdings auch gern einer deiner Versuche sein)
könntest du mir eine Seite eventuell empfehlen wo ich mich reinlesen könnte für das Thema, wie zB. mit dem Unterschied zwischen Serial.write(x) und Serial.println(x)…
Außerdem dass der Rückgabewert von read() nur int ist weil auch -1 für nichts kommen kann. Gelesen wird aber eigentlich nur ein Byte
Generell verwendet man print() für ASCII und write() für Binär-Daten. Man kann mit write() und read() auch Integer mit mehrere Bytes versenden, aber dafür muss man sie in ihre Einzel-Bytes zerlegen
ich hab es genau so wie auf der Seite die du mir geschickt hast und ich versteh es einfach nicht... ich verstehe nicht was ich falsch mache... ich hab mir gefühlt TAUSENDE Youtube Videos angeschaut und genau so es gemacht aber es funktioniert nicht....
1.) Wenn du Serial.print() machst wird die Zahl in einen ASCII Text konvertiert. Dann kommt am anderen Ende auch ASCII an. z.B. 100 wird zu drei Bytes für die drei Zeichen '1', '0' und '0'
Das kann man machen, aber dann muss man das auch als Text einlesen und wieder in eine Zahl konvertieren
2.) Wenn du rein Binär-Daten senden willst musst du Multi-Byte Integer auf einzelne Bytes aufteilen und wieder zusammensetzen
Binär kann u.U. einfacher sein, aber man bekommt eventuell Probleme mit der Synchronisation.
Nun, es ist nicht so schwer - aber es scheint Dir (noch) so.
Ich persönlich bin der Meinung, dass Videos da nicht helfen; Du musst ein klein wenig investieren in Dein Wissen über Datentypen.
int x = 0;
Das bedeutet, dass Du zwei Bytes für x reservierst und damit von -32768 bis +32767 zählen kannst.
Diese Information findet sich hier: int - Arduino-Referenz
Serial.println(x);
Das ergibt die schöne richtige Ausgabe Deines Zählers im seriellen Monitor. Erklärt ist das hier: Serial.println() - Arduino-Referenz
Der spannende Absatz ist der hier: val: Der zu druckende Wert. Erlaubte Datentypen: Beliebiger Datentyp.
Die println-Methode der Klasse Serial nimmt Dir also für alle Datentypen die Umwandlung in Zeichen ab.
mySerial.write(x);
Jetzt nehmen wir den Link von Peter zur Hand und finden darin den Verweis darauf, dass es genauso funktioniert wie Serial.write(). Das habe ich auf die Schnelle nicht auf Deutsch gefunden - auf Dauer kommst Du um das Lesen englischer Dokumentation sowieso nicht drumrum.
Darin: Serial.write(val) val: a value to send as a single byte.
So und da haben wir das Problem: Es wird nur ein Byte Deines int geschickt.
Diese eine Byte liest Du im Mastercode ein und gibst es mit Serial.print - da ohne neue Zeile - aus.
Die LCD lassen wir erstmal weg.
Das bedeutet: Von 0 bis 255 sollte alles gut sein, danach wird es falsch.
Entspricht das soweit Deinen Beobachtungen?
Was genau "falsch" ist hast Du uns ja noch nicht mitgeteilt.
Diese eine Byte liest Du im Mastercode ein und gibst es mit Serial.print - da ohne neue Zeile - aus.
Das was davor steht habe ich alles verstanden und gemacht (also das mySerial.write(x) gegen mySerial.print(x) austaschen) , aber wie krieg ich es hin das er dann beide Bytes abließt und die richtige Antwort auf dem LCD Bildschirm wiedergibt?
Das bedeutet: Von 0 bis 255 sollte alles gut sein, danach wird es falsch.
Entspricht das soweit Deinen Beobachtungen?
Was genau "falsch" ist hast Du uns ja noch nicht mitgeteilt.
Also ich will am Ende meines Projektes einen Wert Zwischen 0 und 30 ausgeben.
Momentan bin ich aber daran, das ich am Slave eine Variable von 0 im Sekundentakt raufzählen lasse, aber am Master komische Zahlen im Seriellen Monitor als auch am LCD Bildschirm bekomme (zB 10, 13, 49, 152 und manchmal Zahlen über 255)
Momentaner Slave Code:
#include<SoftwareSerial.h>
const int rxPin = 10;
const int txPin = 11;
int x = 0;
SoftwareSerial mySerial(rxPin,txPin);//RX,TX.
void setup()
{
Serial.begin(9600);
mySerial.begin(9600);
}
void loop()
{
x = 18;
Serial.println(x);
mySerial.print(x);
delay (1000);
}
(also das mySerial.write(x) gegen mySerial.print(x) austaschen)
Du musst den Unterschied verstehen damit du eine qualifizierte Entscheidung treffen kannst. ASCII wird aber in der Tat wahrscheinlich einfacher sein. Und es passt auch zur Anwendung mit der Anzeige
void loop()
{
char* str = readLine(mySerial);
if (str != nullptr)
{
int data = atoi(str);
Serial.println(data);
//Alternative zur direkten Ausgabe des Texts ohne Konvertierung: Serial.println(str) bzw. lcd.print(str)
}
}
Dann musst du aber beim Senden println() machen, damit ein Linefeed hinten angehängt wird!
Und READ_BUFFER_SIZE kann man etwas nach unten setzten
Wenn du mit dem Wert nichts rechnen musst, sondern ihn direkt auf einem Display drucken willst kannst du dir auch die Konvertierung mit atoi() sparen und direkt lcd.print(str) machen. Ich habe das nur der vollständig halber gezeigt wie man wieder auf einen Integer kommt
10 ist LF
13 ist CR
Vll. schon Mal gehört ... Zeilenende CR/LF
49 ist eine Null ('0') - oder als HEX 0x30 (Nebeninfo).
Die 156 müsste ich recherchieren ... denke, wird das Komma (Trennzeichen) sein.
Wieso lässt Du Dir auf dem Empfänger die empfangenen Werte nicht sowohl als Zeichen (wenn >32) wie als HEX ausgeben?
Eine 100 wäre z.B. '1' '0' '0'
Oder 49 48 48
HEX 0x31 0x30 0x30
Wenn Du den Kram wieder einliest, hast Du als Erstes eine 1.
Die merken wir uns. (bzw. den Wert 1, also Zeichen-0x30)
Dann lesen wir eine Null - oha, ein weiteres Zeichen.
Wie bekommen wir aus einer 1 eine Zehn? Jupp, per x10!
Wert*=10;
Wert+=neuerWert-0x30 (wir müssen von der '0' noch den ASCII-Wert des Zeichen abziehen, um auf 0 zu kommen).
Wenn nun ein 'Nicht Zahl-Zeichen' gefunden wird, ist die Zahl fertig.
Man kann den Kram auch in ein char-Array einlesen und irgendwelche Funktionen drüber prügeln - Da bin ich aber draußen (bzw. müsste Googel bemühen - wird wohl atoi sein)
0-31 sind Steuerzeichen. Hier ist eben vor allem CR + LF wichtig. Die zwei Codes sollte man in Dezimal und Hex kennen. Tab kommt manchmal auch vor. Der Rest ist selten.
Danach kommen druckbare Zeichen. Hier ist es sinnvoll zu wissen dass bei Ziffern 0 zu 48 oder 0x30 wird. Dadurch kann man ASCII Codes einfach durch Substraktion in eine Integer Ziffer konvertieren
Und ja, man kann auch einfach aufaddieren. Wenn man immer nur positive Zahlen überträgt geht das sehr schön. Ist aber auch nicht sehr flexibel.
Die Serial Klasse hat auch eingebaute Methoden um Zahlen aus ASCII Text zu lesen, aber das ist nicht so schön wie mein Code da das blockiert. Da wird einfach gewartet bis eine Zeit lang nichts mehr kommt statt auf ein Endzeichen abzufragen. Bei manchen Anwendungen ist das aber vertretbar
Die 0 wird zu 0x30 (48 Dez = 30 Hex - hier ist die letzte Ziffer der Zahlenwert, kann man sich merken).
48=3x16 + 0x1 - daher vorne die Drei und hinten die Null (Basis der HEX-Zahlensystem ist die 16, daher der Name HexaDezimal)
Ich bin sehr dankbar, das ihr euch zeit nimmt mir zu helfen... aber ich werde einfach ich schlau daraus was ihr für tipps schreibt. Ich bin quasi Anfänger in dem Bereich und ich habs mit mikrokontrollern nicht so einfach. Ich möchte selbst dieses beschissene Problem aus der Welt bekämpfen aber ich werde aus manchen sachen nicht schlau und verzweifele schon seit Wochen daran... tut mir leid
Aber andererseits kann ich nicht verstehen warum es so kompliziert (zumindest für mich), blöde daten zu übertragen, oder zumindest einen Text (zb "Hallo") zu übertragen.
Nun, die Kollegen haben schon eine Reihe weiterer Hilfestellungen gegeben.
Alternative 1:
Wenn Du für Dein Projekt sowieso nur Zahlen zwischen 0 und 30 ausgeben willst, reicht statt
int x = 0;
auch ein schnödes
byte x = 0;
Alternative 2 (binäre Übertragung):
(Deshalb sprach ich vom Wissen über Datentypen)
Ein int (zwei Byte) kann mittels Einzel-Übertragung beider Bytes auf der anderen Seite ankommen.
Die musst Du Dir aber erst ausrechnen:
int x = 0;
...
// in der loop()
byte x_lsb = x % 256; // niederwertiges Byte (least significant) = untere Hälfte
byte x_msb = x / 256; // höherwertiges Byte (most significant) = obere Häfte
// Das geht auch schneller mit Bitoperationen:
byte x_lsb = x & 0xFF; // niederwertiges Byte (least significant) = untere Hälfte
byte x_msb = x >> 8; // höherwertiges Byte (most significant) = obere Häfte
...
mySerial.write(x_lsb);
mySerial.write(x_msb);
...
Und auf der anderen Seite (master) solltest Du dann die beiden Bytes einzeln aufsammeln und dann das int wieder zusammensetzen.
So in etwa (nicht wirklich schön und ich habe das jetzt auch nicht getestet):
// in der loop()
byte value;
int x = 0;
// Dieser Code setzt voraus, dass man mit dem Lesen nicht zufällig mitten zwischen die beiden Bytes
// geraten ist und dass keine weitere Information ausser den beiden Nutzbytes übertragen wird.
while (mySerial.available() > 0) {
value = mySerial.read(); // lies das LSB
x = value;
value = mySerial.read(); // lies das MSB
x |= (int)value << 8;
}
// jetzt erst x auf LCD schreiben
Bei der ASCII-Übertragung und den "println"-Funktionen musst Du mit den Zeilenumbrüchen CR und LF hantieren; das hat aber den Vorteil, dass Du ein definiertes Ende einer einzelnen Übertragung hast.
Nachteil: die Zahl liegt nicht als Zahl, sondern als Zeichenkette vor (aber das wurde schon beschrieben).