Daten via Bluetooth mit Arduino emfangen und in variablen schreiben ?

Hallo

Ich bin auf ein kleines Verständnis Problem bei meinem Projekt gestoßen und möchte euch um Hilfe bitten.

Folgender Aufbau:

1 x Arduino Mini Pro mit HC05 (Slave) und 5 DYP-Me007 Sensoren
1x Arduino Mega mit HC-05 (Master)

Der Slave sendet die Sensor Daten per Bluetooth an den Master.

TeilCode vom slave:

 for (uint8_t i = 0; i-1 < SONAR_NUM; i++) {
    
  //Serial.print(i);
    //BTserial.println(i);
  
   
    
    Serial.print(cm[i]);
      BTserial.print(cm[i]); 
    Serial.print(",");
      BTserial.print(","); 
      delay(100);
      
  }
  Serial.println();
   BTserial.println();

Beim Master kommt dann am Serial Monitor z.B: folgendes an:
0,0,0,0,45,

Jeder dieser Werte ist ein Sensor Wert der durch ein Komma getrennt ist.
Der Grund warum bei den ersten vier Werten 0 angezeigt wird liegt nur daran das momentan nur ein Sensor angeschlossen ist.

Master code:

#include <SoftwareSerial.h>

SoftwareSerial BTSerial(10, 11); // RX | TX
char empfangeneDaten;


void setup()
{
  pinMode(9, OUTPUT);  // this pin will pull the HC-05 pin 34 (key pin) HIGH to switch module to AT mode
  digitalWrite(9, HIGH);
  Serial.begin(9600);
  Serial.println("Enter AT commands:");
  BTSerial.begin(38400);  // HC-05 default speed in AT command more
}

void loop()
{
  // Keep reading from HC-05 and send to Arduino Serial Monitor
  if (BTSerial.available()) {
    //Serial.write(BTSerial.read());

    empfangeneDaten = BTSerial.read();
Serial.write(empfangeneDaten);

Und genau da beginnen meine Probleme:

Ich möchte jeden dieser Werte in eine eigene int Variable schreiben mit der ich danach in meinem code arbeiten kann.

Bei empfangeDaten handelt es sich doch um den Variablen-Typ Char welcher nur ein Byte belegt.
warum kann ich dann mit: Serial.write(empfangeneDaten); den ganzen String ausgeben wenn diese Variable eigentlich nur einen Buchstaben speichern kann ?

Ich habe versucht empfangeneDaten mit String empfangeneDatenString(empfangeneDaten); in einen String umzuwandeln, was auch funktioniert hat, aber wenn ich mir dann mit firstComma = empfangeneDaten.indexOf(','); die Position des ersten Komma holen will und diese auf dem Serial Monitor ausgeben will bekomme ich Werte wie:
0-1
,0
0-1
,0
0-1
,0
0-1
,0
4-1
6-1
,0

-1

Hat vielleicht jemand eine Idee wie man das Problem lösen könnte ?
Danke schon mal im Voraus.

Ersteinmal solltest du, wenn du SoftwareSerial nutzt, die Geschwindkeit drastisch reduzieren (<= 9600 Baud) oder aber AltSoftSerial testen.
http://forum.arduino.cc/index.php?topic=364652.0
Hier habe ich kleines Beispiel für strtok() eingebaut.

Du gibts mit write jedesmal nur ein char aus. (jedesmal in available() wahr ist). Somit hast du jedes Zeichen gelesen und direkt wieder ausgegeben. An deiner Console/Terminal/Monitor sind immer nur einzelne Zeichen gesendet worden

Hier sind zwei Varianten:
http://forum.arduino.cc/index.php?topic=359203.msg2486412#msg2486412

Einmal erst ein C String einlesen (d.h ein null-terminierstes char Array. Nicht die unnütze String Klasse) und dann konvertieren. Dabei wird jede Zeile mit einem Linefeed abgeschlossen (das println() am Ende passt da). Oder die Ziffern wie sie kommen aufaddieren. Dabei wird der gesendete String in Spitze Klammern <> gepackt.

Bei der C String Variante kann man den Parser aber auch so schreiben wenn man weiß dass man immer die gleiche Anzahl an Werten bekommt:

int var1 = atoi(strtok(stringBuffer, ","));
int var2 = atoi(strtok(NULL, ","));
int var3 = atoi(strtok(NULL, ","));
...

So hat man sie dann in getrennten Variablen statt einem Array. Das ist aber unabhängig vom Einlesen selbst. Das ist ein Vorteil der String Variante: Einlesen und Parsen sind zwei voneinander getrennte Funktionen und man kann einfach den Parser ändern um ein anderes Format zu bearbeiten.

Danke für eure Hilfe aber leider bin ich noch nicht zum gewünschten ziel gekommen. Ich bin mit programmieren leider noch nicht so vertraut.

@sschultewolter baud hab ich auf 9600 reduziert und bei den bluetooth modulen mit AT+UART=9600,0,0 gesetzt.

@serenifly
Ich habe mich dazu entschlossen es mit deiner String variante zu versuchen aber iwas mach ich falsch.
im serial monitor kommt immer nur:
00
0
0
0
0
0

,0
0
0
0
0
0

Wenn ich deinen teil auskommentiere und nur mehr: if (BTSerial.available()) {
Serial.write(BTSerial.read()); verwende bekomme ich das über den serial.
Wo jetzt aufeinmal die letzen Zahlen(44787) herkommen weis ich auch nicht.

0,0,0,0,48,44787,
0,0,0,0,48,45287,
0,0,0,0,48,45787,
0,0,0,0,48,46287,
0,0,0,0,48,46787,

Der ganze Master Code:

#include <SoftwareSerial.h>

SoftwareSerial BTSerial(10, 11); // RX | TX

const int SERIAL_BUFFER_SIZE = 30;
const int MAX_VALUES = 6;

char serialBuffer[SERIAL_BUFFER_SIZE];
int values[MAX_VALUES];

void setup()
{
  pinMode(9, OUTPUT);  // this pin will pull the HC-05 pin 34 (key pin) HIGH to switch module to AT mode
  digitalWrite(9, HIGH);
  Serial.begin(9600);
  Serial.println("Enter AT commands:");
  BTSerial.begin(9600);  // HC-05 default speed in AT command more
}

void loop()
{
  
   if (BTSerial.available()) {
  // Keep reading from HC-05 and send to Arduino Serial Monitor
  delay(50);
    Serial.write(BTSerial.read());

    //empfangeneDaten = BTSerial.read();
//Serial.write(empfangeneDaten);

    parseSerial();

    for (int i = 0; i < MAX_VALUES; i++)
      Serial.println(values[i]);
    Serial.println("---");
  
}
}
bool readSerial(Stream& stream)
{
  static byte index;

  while (BTSerial.available())
  {
    char c = BTSerial.read();

    if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == '\n' && index > 0)
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;

}

void parseSerial()
{
int Sensor1 = atoi(strtok(serialBuffer, ","));
int Sensor2 = atoi(strtok(NULL, ","));
int Sensor3 = atoi(strtok(NULL, ","));
int Sensor4 = atoi(strtok(NULL, ","));
int Sensor5 = atoi(strtok(NULL, ","));

      
  }

Der ganze Slave Code:

#include <SoftwareSerial.h>
SoftwareSerial BTserial(12, 11); // RX | TX
// Connect the HC-05 TX to Arduino pin 2 RX.
// Connect the HC-05 RX to Arduino pin 3 TX through a voltage divider.
//
char c = ' ';


// ---------------------------------------------------------------------------
// This example code was used to successfully communicate with 15 ultrasonic sensors. You can adjust
// the number of sensors in your project by changing SONAR_NUM and the number of NewPing objects in the
// "sonar" array. You also need to change the pins for each sensor for the NewPing objects. Each sensor
// is pinged at 33ms intervals. So, one cycle of all sensors takes 495ms (33 * 15 = 495ms). The results
// are sent to the "oneSensorCycle" function which currently just displays the distance data. Your project
// would normally process the sensor results in this function (for example, decide if a robot needs to
// turn and call the turn function). Keep in mind this example is event-driven. Your complete sketch needs
// to be written so there's no "delay" commands and the loop() cycles at faster than a 33ms rate. If other
// processes take longer than 33ms, you'll need to increase PING_INTERVAL so it doesn't get behind.
// ---------------------------------------------------------------------------
#include <NewPing.h>

#define SONAR_NUM     5 // Number of sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 100 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(2, 3, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(4, 5, MAX_DISTANCE),

  NewPing(6, 7, MAX_DISTANCE),
  NewPing(8, 9, MAX_DISTANCE),
  NewPing(10, 13, MAX_DISTANCE),

};

void setup() {
  Serial.begin(115200);
  Serial.println("Enter AT commands:");
  BTserial.begin(9600);
  //Sensoren
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
      if (i == 0 && currentSensor == SONAR_NUM - 1) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      sonar[currentSensor].timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentSensor = i;                          // Sensor being accessed.
      cm[currentSensor] = 0;                      // Make distance zero in case there's no ping echo for this sensor.
      sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }
  //delay(1000);
  // Other code that *DOESN'T* analyze ping results can go here.
}

void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
}

void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  // The following code would be replaced with your code that does something with the ping results.

  //Serial.print('<');
  //BTserial.print('<');
  for (uint8_t i = 0; i - 1 < SONAR_NUM; i++) {

    //Serial.print(i);
    //BTserial.println(i);



    Serial.print(cm[i]);
    BTserial.print(cm[i]);
    Serial.print(",");
    BTserial.print(",");
    delay(100);

  }
  Serial.println();
   BTserial.println();
}

Vielleicht könnt ihr mir anhamd davon sagen wo das Problem liegt. Ich bin echt ratlos, aber kann ja nur besser werden =)

Danke.

Oh je. Das in loop() ist totaler Quatsch. Du musst deine neue Einlese Funktion auch verwenden! So steht es doch dort:

void loop()
{
  if (readSerial(BTserial) == true)
  {
    parseSerial();

    for (int i = 0; i < MAX_VALUES; i++)
      Serial.println(values[i]);
    Serial.println("---");
  }
}

Die Idee dabei ist, die Funktion ständig aufzurufen und nachzuschauen ob Daten da sind und dann ob das Ende des Strings da ist. Solange das nicht zutrifft gibt man false zurück. Wenn man fertig ist bekommt man true und wertet den String aus

Außerdem habe ich readSerial() so geschrieben, dass eine Referenz auf einen Stream übergeben wird. Man kann also HardwareSerial, SoftwareSerial, AltSoftSerial, SD oder Ethernet übergeben. Es ist daher unnötig da in der Funktion selbst BTSerial explizit anzugeben. Die Quelle wird beim Funktionsaufruf übergeben.

Und nirgendswo sonst BTSerial.read() machen!!

Dann musst du dich beim Parsen entscheiden ob du das in einem Array oder in einzelnen Variablen haben willst. Das Array ist global. Deine einzelnen Variablen sind nur lokal und existieren außerhalb der Funktion nicht. Das kann man so machen. Dann musst du die Variablen aber auch in der Parse Funktion ausgeben, da sie danach aufhören zu existieren. Oder du machst sie global.

Aber wenn du nicht in der Lage bist zu erkennen dass das zwei verschiedene Sachen sind, dann bleibe erst mal bei der Array Variante. Zumindest bis das Einlesen funktioniert. Dann hast du nicht zwei Fehlerquellen. Also erst mal eine Baustelle schaffen (das Einlesen) und danach die Bearbeitung anpassen wenn du es anders willst.

Du hast es im Moment so dass du die konvertierten Zahlen in lokale Variablen schreibst und dann aber das globale Array ausgibst. Da kommt natürlich nichts dabei heraus. Verwende wie gesagt erst mal die Parse Funktion aus dem Thread oben, die das in einer Schleife in das Array schreibt.

Das delay(100) beim Senden ist übrigens überflüssig

Danke dir. Ich hab mal versucht Ich hab den Code jetzt so abgeändert:

#include <SoftwareSerial.h>

SoftwareSerial BTSerial(10, 11); // RX | TX

const int SERIAL_BUFFER_SIZE = 30;
const int MAX_VALUES = 5;

char serialBuffer[SERIAL_BUFFER_SIZE];
int values[MAX_VALUES];

void setup()
{
  pinMode(9, OUTPUT);  // this pin will pull the HC-05 pin 34 (key pin) HIGH to switch module to AT mode
  digitalWrite(9, HIGH);
  Serial.begin(9600);
  Serial.println("Enter AT commands:");
  BTSerial.begin(9600);  // HC-05 default speed in AT command more
}

void loop()
{
  
   if (readSerial(Serial) == true) {
  // Keep reading from HC-05 and send to Arduino Serial Monitor

    //Serial.write(BTSerial.read());

    //empfangeneDaten = BTSerial.read();
//Serial.write(empfangeneDaten);
//delay(5);
    parseSerial();

    for (int i = 0; i < MAX_VALUES; i++)
      Serial.println(values[i]);
    //Serial.println("---");
  
}
}

JUHU =)
Am Serial Monitor bekomme ich jetzt schon mal:
0
0
0
0
40

wie kann ich die Werte des Arrays jetzt außerhalb der for-Schleife verwenden ? bzw. wie bekomme ich die jetzt in Variablen die ich außerhalb verwenden kann?

Sorry für die blöden Fragen, aber so ganz blick ich da noch nicht durch.
Danke für die nette Unterstützung.

trunkx:
wie kann ich die Werte des Arrays jetzt außerhalb der for-Schleife verwenden ? bzw. wie bekomme ich die jetzt in Variablen die ich außerhalb verwenden kann?

Wie man halt ein Array verwendet. Über den Index darauf zugreifen. So wie auch hier:

    for (int i = 0; i < MAX_VALUES; i++)
      Serial.println(values[i]);

Oder sowas direkt ohne Schleifen-Variable:

Serial.println(values[0]);

Wenn du statt Indizes sprechende Variablen willst musst du die global anlegen:

int sensor1;
int sensor2;
...

Und dann machst du in parseSerial() das:

void parseSerial()
{
  sensor1 = atoi(strtok(serialBuffer, ","));
  sensor2 = atoi(strtok(NULL, ","));
  ...
}

Wenn die Variablen eh nur von 1 bis n benannt sind, kann man auch Array Indices von 0 bis n - 1 nehmen. Das ist aber Geschmackssache

Hier liest du jetzt von Serial ein:

if (readSerial(Serial) == true)

Wenn du dann von BT lesen willst, machst du einfach das:

if (readSerial(BTserial) == true)

:slight_smile:
Genau damit das geht ist der Parameter eine Referenz auf die Stream Klasse und nicht die Serial Klasse

Lerne dabei den Unterschied zwischen globalen und lokalen Variablen:

Super danke es funzt =)

Habe es mit:

 if (values[4] <= 55){
  digitalWrite(led,HIGH);  
  }else{
    digitalWrite(led,LOW);
    }

versucht und es funktioniert.

Das mit den globalen und lokalen Variablen schau ich mir an. Danke für den Tipp.