String der via Python gesendet wird im C++ zerlegen



  • Hallo. Ich sende über Python einen String bei dem die Wörter mit einem ; getrennt sind. Dieser muss bei meinem C++ Skript empfangen und zerlegt werden und danach Variablen zugewiesen, die ich verwende um einen Sensor neu zu initialisieren. Hier der Python Code:

                    print(Variable1)
                    # String erstellen
                    str_parameters = f"{Variable1},{Variable2},{Variable3},{Variable4},{Variable5},{Variable6}"
    
                    # Byte-Array erstellen
                    byte_array = str_parameters.encode('ascii')
    
                    print("Sende Werte")
                    # Array senden
                    await client.write_gatt_char(UUID3, byte_array)
    

    Das zerlegen klappt mit diesem Code:

    / Diese Funktion zerlegt einen String in Teilstrings anhand eines Trennzeichens
    std::vector<std::string> splitString(std::string s, std::string delimiter) {
      std::vector<std::string> result;
      size_t pos = 0;
      std::string token;
      while ((pos = s.find(delimiter)) != std::string::npos) {
        token = s.substr(0, pos);
        result.push_back(token);
        s.erase(0, pos + delimiter.length());
      }
      result.push_back(s);
      return result;
    }
    

    Diese "Zerlegefunktion" wird aufgerufen, sobal ein Array empfangen wird. Die Parameter werden nun geprintet (das klappt) und müssten einer Variablen (string?) zugewiesen werden, damit ich dann den Sensor initialisieren kann, aber genau da hänge ich nun.

    class MyCallbacks : public BLECharacteristicCallbacks {
        void onWrite(BLECharacteristic *pCharacteristic) {
          std::string value = pCharacteristic->getValue();
          std::vector<std::string> parameters = splitString(value, " ");
          for (int i = 0; i < parameters.size(); i++) {
            Serial.print("Parameter empfangen");
            Serial.print(i + 1);
            Serial.print(": ");
            Serial.println(parameters[i].c_str());
     
            if (i == 0) {
              ledBrightness1 = std::strtoul(parameters[i].c_str(), nullptr, 10);
     
     
            }
     
            if (i == 1) {
              //std::string sampleAverage1 = parameters[i];
              // Umwandlung des std::string-Objekts in einen C-String
     
              sampleAverage1 = parameters[i];  // Aktualisiere die globale Variable
     
            }
     
            if (i == 2) {
     
              std::string ledMode1 = parameters[i];
            }
     
            if (i == 3) {
              std::string sampleRate1 = parameters[i];
     
            }
     
            if (i == 4) {
              std::string pulseWidth1 = parameters[i];
            }
     
            if (i == 5) {
              std::string adcRange1 = parameters[i];
            }
     
     
     
     
     
            Serial.print("printe Parameter einzeln: ");
            Serial.print(ledBrightness1);
            Serial.print("; ");
            Serial.print("The one  = ");
            Serial.print(sampleAverage1.c_str());
     
          }
     
     
          std::string received_message = "Werte empfangen!";
          pCharacteristic->setValue(received_message);
          pCharacteristic->notify();
          updateSensor = true;
        }
     
    };
    

    Kann mir da wer helfen?

    Der Sensor würde dann hier neu initialisiert:

    void updateSensorConfig() {
      particleSensor.sensorConfiguration(ledBrightness1, sampleAverage1, ledMode1, sampleRate1, pulseWidth1, adcRange1);
      Serial.println("Sensor configuration updated");
     }
    


  • Ich habe mehrere Bemerkungen:

    Am wichtigsten ist vermutlich, dass ich den Zweck der Schleife über die parameters nicht verstehe. Eine Schleife, bei der du dann jeweils auf i == 0 bis i == 5 testest, erscheint irgendwie nicht sinnvoll. Schlimmer noch: ich verstehe nicht, was der Code machen soll:

    • bei i == 1 schreibst du im Kommentar irgendwas von einem C-String (wo?) und dann wird eine globale Variable aktualisiert (sind globale Variablen wirklich nötig?)
    • bei i == 2 bis i == 5 tut der Schleifenkörper jeweils gar nichts. Du weist einer neuen String-Variable den Wert aus parameters[i] zu, aber diese Variable geht sofort wieder out of scope. Was soll das also werden?

    Dann noch zum Rest:

    • str_parameters = f"{Variable1},{Variable2},{Variable3},{Variable4},{Variable5},{Variable6}" - hast du wirklich Variablen mit diesen Namen oder sind die fürs Beispiel hier umbenannt?
    • dein C++ splitString sieht nicht effizient aus (aber richtig, sofern ich das sehe)
    • du behandelst nicht den Fall, dass eine deiner Variablen den Delimiter enthalten könnte
    • Der Python-String scheint mit Kommas zusammengebastelt zu werden, aber in C++ splittest du an Leerzeichen (std::vector<std::string> parameters = splitString(value, " ");). Das passt nicht zusammen.


  • @wob sagte in String der via Python gesendet wird im C++ zerlegen:

    • Der Python-String scheint mit Kommas zusammengebastelt zu werden, aber in C++ splittest du an Leerzeichen … Das passt nicht zusammen.

    In der Beschreibung vom TO steht sogar was von ;



  • Hallo, da ist irgendwas schief gelaufen, hatte das 2 mal verschieden gecodet und irgendwie sind da noch Reste vom alten Versuch über geblieben. Hab das nun ausgebessert, nur leider immer noch einen Fehler. Hier mal der ganze Code:

    #include <BLEDevice.h>
    #include <BLEServer.h>
    #include <BLEUtils.h>
    #include <BLE2902.h>
    #include <DFRobot_MAX30102.h>
    #include <string> // Header für std::stoi und std::stoul
    #include <stdlib.h>
     
     
    DFRobot_MAX30102 particleSensor;
     
    int Wert; // Variable die bei jedem Erfassen der Daten erhöht wird um dann bei 100 Werten das Array abzuschließen und das Senden einzuleiten
     
    // Datenarray zum Senden von Daten über das BLE-Charakteristikum
    int data[100] = {0};  // 100 Integer-Werte, initialisiert mit 0
    int dataIR[100] = {0};  // 100 Integer-Werte, initialisiert mit 0
     
    BLEServer* pServer = NULL;
    BLECharacteristic* pCharacteristic = NULL;
    BLECharacteristic* pCharacteristic2 = NULL;
    BLECharacteristic* pCharacteristic3 = NULL;
     
    std::string ledBrightness1 = "50";
     
    std::string sampleAverage1 = "SAMPLEAVG_4";
    std::string ledMode1 = "MODE_MULTILED";
    std::string sampleRate1 = "SAMPLERATE_100";
    std::string pulseWidth1 = "PULSEWIDTH_411";
    std::string adcRange1 = "ADCRANGE_16384";
     
    bool deviceConnected = false;
    bool updateSensor = false;
     
    // See the following for generating UUIDs:
    // https://www.uuidgenerator.net/
     
    #define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
    #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
    #define CHARACTERISTIC_UUID2 "beb5483e-36e1-4688-b7f5-ea07361b26a9"
    #define CHARACTERISTIC_UUID3 "5a464637-3fe1-4685-9e33-ec4ba175f081"
     
     
    class MyServerCallbacks: public BLEServerCallbacks {
        void onConnect(BLEServer* pServer) {
          deviceConnected = true;
        };
     
        void onDisconnect(BLEServer* pServer) {
          deviceConnected = false;
        }
    };
     
    // Diese Funktion zerlegt einen String in Teilstrings anhand eines Trennzeichens
    std::vector<std::string> splitString(std::string s, std::string delimiter) {
      std::vector<std::string> result;
      size_t pos = 0;
      std::string token;
      while ((pos = s.find(delimiter)) != std::string::npos) {
        token = s.substr(0, pos);
        result.push_back(token);
        s.erase(0, pos + delimiter.length());
      }
      result.push_back(s);
      return result;
    }
    
    
     class MyCallbacks : public BLECharacteristicCallbacks {
      void onWrite(BLECharacteristic *pCharacteristic) {
        std::string value = pCharacteristic->getValue();
        std::vector<std::string> parameters = splitString(value, ",");
        
        if (parameters.size() == 6) {
          
          ledBrightness1 = parameters[0];
          sampleAverage1 = parameters[1];
          ledMode1 = parameters[2];
          sampleRate1 = parameters[3];
          pulseWidth1 = parameters[4];
          adcRange1 = parameters[5];
    
     
     
     
            Serial.print("Led Helligkeit = ");
            Serial.print(ledBrightness1.c_str());
            Serial.print("; ");
            Serial.print("Durchschnitt  = ");
            Serial.print(sampleAverage1.c_str());
            Serial.print("; ");
            Serial.print("Led Modus  = ");
            Serial.print(ledMode1.c_str());
            Serial.print("; ");
            Serial.print("Abtastrate  = ");
            Serial.print(sampleRate1.c_str());
            Serial.print("; ");
            Serial.print("Pulsweite  = ");
            Serial.print(pulseWidth1.c_str());
            Serial.print("; ");
            Serial.print("ADC Range  = ");
            Serial.print(adcRange1.c_str());
     
          }
     
     
          std::string received_message = "Werte empfangen!";
          pCharacteristic->setValue(received_message);
          pCharacteristic->notify();
          updateSensor = true;
        }
     
    };
     
    void updateSensorConfig() {
      particleSensor.sensorConfiguration(ledBrightness1, sampleAverage1, ledMode1, sampleRate1, pulseWidth1, adcRange1);
      Serial.println("Sensor configuration updated");
     }
     
    void setup() {
      Serial.begin(115200);
     
      while (!particleSensor.begin()) {
        Serial.println("MAX30102 was not found");
        delay(1000);
      }
     
      particleSensor.sensorConfiguration(50, SAMPLEAVG_4, MODE_MULTILED, SAMPLERATE_1000, PULSEWIDTH_411, ADCRANGE_16384);
     
      BLEDevice::init("ESP_SPO2");
     
      pServer = BLEDevice::createServer();
      pServer->setCallbacks(new MyServerCallbacks());
     
      BLEService *pService = pServer->createService(SERVICE_UUID);
     
      pCharacteristic = pService->createCharacteristic(
                          CHARACTERISTIC_UUID,
                          BLECharacteristic::PROPERTY_READ   |
                          BLECharacteristic::PROPERTY_WRITE  |
                          BLECharacteristic::PROPERTY_NOTIFY |
                          BLECharacteristic::PROPERTY_INDICATE
                        );
     
      pCharacteristic2 = pService->createCharacteristic(
                           CHARACTERISTIC_UUID2,
                           BLECharacteristic::PROPERTY_READ   |
                           BLECharacteristic::PROPERTY_WRITE  |
                           BLECharacteristic::PROPERTY_NOTIFY |
                           BLECharacteristic::PROPERTY_INDICATE
                         );
     
      pCharacteristic3 = pService->createCharacteristic(
                           CHARACTERISTIC_UUID3,
                           BLECharacteristic::PROPERTY_READ   |
                           BLECharacteristic::PROPERTY_WRITE  |
                           BLECharacteristic::PROPERTY_NOTIFY |
                           BLECharacteristic::PROPERTY_INDICATE
                         );
     
      pCharacteristic3->setCallbacks(new MyCallbacks());
     
      pCharacteristic->addDescriptor(new BLE2902());
      pCharacteristic2->addDescriptor(new BLE2902());
      pCharacteristic3->addDescriptor(new BLE2902());
     
      pService->start();
     
      BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
      pAdvertising->addServiceUUID(SERVICE_UUID);
      pAdvertising->setScanResponse(false);
      pAdvertising->setMinPreferred(0x0);
      BLEDevice::startAdvertising();
      Serial.println("Waiting for a client connection to notify...");
    }
     
    void loop() {
      if (Wert <= 99) {
        //Serial.println(Wert);
        data[Wert] = particleSensor.getRed();
        dataIR[Wert] = particleSensor.getIR();
        Wert = Wert + 1;
      } else {
        Wert = 0;
        //Serial.println("100 Werte im Array gesammelt");
        //Serial.println("Bin in vor Schleife Gerät Verbunden");
        //Serial.println();
        pCharacteristic->setValue((uint8_t*)data, sizeof(data));
        pCharacteristic2->setValue((uint8_t*)dataIR, sizeof(dataIR));
        pCharacteristic->notify();
        pCharacteristic2->notify();
        //Serial.println("Sende Daten:");
     
        if (!deviceConnected) {
          Serial.println("ESP nicht verbunden. Daten: ");
     
          for (int i = 0; i < 100; i++) {
            Serial.print(data[i]);
            Serial.print(" ");
            Serial.println();
          }
        }
      }
     
      if (!deviceConnected) {
        delay(500);
        pServer->startAdvertising();
        Serial.println("start advertising");
      }
     
      if (updateSensor) {
        //    updateSensorConfig();
        updateSensor = false;
      }
    }
    
    

    Geprintet wird das nun korrekt, nur kann ich den Sensor nicht updaten, da bekomme ich:

    Arduino: 1.8.19 (Windows Store 1.8.57.0) (Windows 10), Board: "ESP32 Dev Module, Disabled, Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS), 240MHz (WiFi/BT), QIO, 80MHz, 4MB (32Mb), 921600, None"
    
    In function 'void updateSensorConfig()':
    
    : no matching function for call to 'DFRobot_MAX30102::sensorConfiguration(std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&)'
    
       particleSensor.sensorConfiguration(ledBrightness1, sampleAverage1, ledMode1, sampleRate1, pulseWidth1, adcRange1);
    
                                                                                                                       ^
    
    C:\Users\emanu\Documents\Arduino\libraries\DFRobot_MAX30102-master\src/DFRobot_MAX30102.h:252:8: note: candidate: void DFRobot_MAX30102::sensorConfiguration(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)
    
       void sensorConfiguration(uint8_t ledBrightness = 0x1F, uint8_t sampleAverage = SAMPLEAVG_4, \
    
            ^
    
    C:\Users\emanu\Documents\Arduino\libraries\DFRobot_MAX30102-master\src/DFRobot_MAX30102.h:252:8: note:   no known conversion for argument 1 from 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'uint8_t {aka unsigned char}'
    
    exit status 1
    
    no matching function for call to 'DFRobot_MAX30102::sensorConfiguration(std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&, std::__cxx11::string&)'
    

    Die cpp und h die ich nutze findet sich hier:

    https://github.com/DFRobot/DFRobot_MAX30102/blob/master/src/DFRobot_MAX30102.cpp
    https://github.com/DFRobot/DFRobot_MAX30102/blob/master/src/DFRobot_MAX30102.h

    Der Sensor wird aber doch eigentlich mit einem String initialsiert oder?

    Der Code funktioniert:

    particleSensor.sensorConfiguration(50, SAMPLEAVG_4, MODE_MULTILED, SAMPLERATE_1000, PULSEWIDTH_411, ADCRANGE_16384);
    


  • Du übergibst bei

    particleSensor.sensorConfiguration(ledBrightness1, sampleAverage1, ledMode1, sampleRate1, pulseWidth1, adcRange1);
    

    string-Werte, aber die Methode erwartet uint8_t (also Zahlenwerte).
    Du mußt also noch stoi o.ä. jeweils aufrufen.



  • @Th69 sagte in String der via Python gesendet wird im C++ zerlegen:

    Du übergibst bei

    particleSensor.sensorConfiguration(ledBrightness1, sampleAverage1, ledMode1, sampleRate1, pulseWidth1, adcRange1);
    

    string-Werte, aber die Methode erwartet uint8_t (also Zahlenwerte).
    Du mußt also noch stoi o.ä. jeweils aufrufen.

    Danke für die Antwort. Das hatte ich auch vermutet, aber der Sensor wird ja mit Begriffen und Zahlen initialisiert, wie geht das zusammen? Siehe:

    "particleSensor.sensorConfiguration(50, SAMPLEAVG_4, MODE_MULTILED, SAMPLERATE_1000, PULSEWIDTH_411, ADCRANGE_16384);"



  • Das sind ja alles nur Zahlen (die Bezeichner dort sind ja per #define erzeugte Werte, z.B. #define SAMPLEAVG_4 2, und werden vom Präprozessor ersetzt, so daß der Compiler nur noch die reinen Zahlenwerte sieht).



  • @Th69 sagte in String der via Python gesendet wird im C++ zerlegen:

    Das sind ja alles nur Zahlen (die Bezeichner dort sind ja per #define erzeugte Werte, z.B. #define SAMPLEAVG_4 2, und werden vom Präprozessor ersetzt, so daß der Compiler nur noch die reinen Zahlenwerte sieht).

    Danke für deine Antwort! Aber um das nochmal zu klären, soll ich nun den Text separat übergeben und die Nummer als Variable oder den Text gleich weglassen? So ganz habe ich das noch nicht verstanden. Beim Ledmodus habe ich ja gar keine Zahl?



  • Jetzt habe ich bei deinem (zuletzt geposteten) Code erst gesehen, daß die Zeilen 23-29 so keinen Sinn ergeben (man kann keinen Makronamen in einem String speichern, um daraus dann direkt den Wert zu extrahieren).
    Am besten, du deklarierst die 6 Variablen als int (bzw. uint_8) und beim Parsen benutzt du dann direkt

    ledBrightness1 = std::stoi(parameters[0]);
    // etc.
    

    Dafür müssen aber auch schon direkt in dem zu parsenden String Zahlenwerte stehen (und nicht die Makronamen), ansonsten müßtest du diese wiederum mittels einer std::map<string, int> o.ä. speichern und daraus dann die Zahlenwerte lesen.

    Wie sieht denn ein konkreter String aus?



  • @Th69 sagte in String der via Python gesendet wird im C++ zerlegen:

    Jetzt habe ich bei deinem (zuletzt geposteten) Code erst gesehen, daß die Zeilen 23-29 so keinen Sinn ergeben (man kann keinen Makronamen in einem String speichern, um daraus dann direkt den Wert zu extrahieren).
    Am besten, du deklarierst die 6 Variablen als int (bzw. uint_8) und beim Parsen benutzt du dann direkt

    ledBrightness1 = std::stoi(parameters[0]);
    // etc.
    

    Dafür müssen aber auch schon direkt in dem zu parsenden String Zahlenwerte stehen (und nicht die Makronamen), ansonsten müßtest du diese wiederum mittels einer std::map<string, int> o.ä. speichern und daraus dann die Zahlenwerte lesen.

    Wie sieht denn ein konkreter String aus?

    Wenn ich dich richtig verstehe meinst du, dass ich in das Array das ich via BLE sende die Zahlen reinschreibe und diese dann den Variablen (in) innerhalb des C++ Sketch zuordne und auf die Namen verzichte, oder? So hätte ich mir das gedacht, aber ich muss dem Sensor ja auch noch den Namen übergeben, oder? Das hätte ich zB mit SAMPLEAVG_+(Variable) versucht, hatte aber nicht geklappt. Ich kann jedenfalls natürlich auch einfach Zahlen schicken und diese dann auch korrekt extrahieren. Bei der ersten Variable (Led Helligkeit = 50 oder 20, 30) klappte das schon.



  • Ja, so meine ich das.



  • @Th69 sagte in String der via Python gesendet wird im C++ zerlegen:

    Ja, so meine ich das.

    Ich habe das nun versucht, aber ich bekomme immer Fehlermeldungen, egal ob Plus oder ohne:

    "'SAMPLERATE_' was not declared in this scope"

    Das habe ich nun so geschrieben SAMPLERATE_+(Variable) oder 'SAMPLERATE_(Variable). Die Variable dürfte nun korrekt den Zahlenwert als Int haben, nur den Konstruktor (?) kann ich irgendwie nicht übergeben. Alternativ hatte ich gedacht im Sensordatenplatt zu schauen wie man den Sensor initialisiert und dann die Funktion selber anlegen. Aber kann das sein? Dass man das nicht anders lösen kann? Oder übersehe ich da was?



  • Du sollst (bzw. mußt) auf die Makronamen verzichten und direkt die Zahlen verwenden.
    Die in DFRobot_MAX30102.h deklarierten Werte sind doch nur Zahlen zwischen 0 und 7.

    Oder werden vom Python-Programm nicht diese Zahlen übergeben, sondern z.B. 1000 bei der Sample Rate?

    Dann mußt du entweder das Python-Programm anpassen oder aber, wie ich schon schrieb, eine Datenstruktur dafür verwenden, z.B.

    std::map<int, int> SampleRates =
    {
        {   50, 0 },
        {  100, 1 },
        {  200, 2 },
        // ...
    };
    

    Und dann per SampleRate[sampleRate1] darauf zugreifen.
    Entsprechend dann für die anderen Parameter ähnliche Datenstrukturen anlegen.



  • @Th69 sagte in String der via Python gesendet wird im C++ zerlegen:

    Du sollst (bzw. mußt) auf die Makronamen verzichten und direkt die Zahlen verwenden.
    Die in DFRobot_MAX30102.h deklarierten Werte sind doch nur Zahlen zwischen 0 und 7.

    Ach jetzt hab ich das kapiert. Ja ich hatte immer die Zahlen 1000 zB übergeben und nicht 5. So macht das nun mehr Sinn, werde ich später dann testen. Stand igw total am Schlauch. 😃 ^^



  • @Th69 sagte in String der via Python gesendet wird im C++ zerlegen:

    Du sollst (bzw. mußt) auf die Makronamen verzichten und direkt die Zahlen verwenden.
    Die in DFRobot_MAX30102.h deklarierten Werte sind doch nur Zahlen zwischen 0 und 7.

    Oder werden vom Python-Programm nicht diese Zahlen übergeben, sondern z.B. 1000 bei der Sample Rate?

    Dann mußt du entweder das Python-Programm anpassen oder aber, wie ich schon schrieb, eine Datenstruktur dafür verwenden, z.B.

    std::map<int, int> SampleRates =
    {
        {   50, 0 },
        {  100, 1 },
        {  200, 2 },
        // ...
    };
    

    Und dann per SampleRate[sampleRate1] darauf zugreifen.
    Entsprechend dann für die anderen Parameter ähnliche Datenstrukturen anlegen.

    Ja das war der Fehler, nun klappt es. Yuhu! Danke und LG


Anmelden zum Antworten