String an Funktion übergeben

Hallo werte Gemeinde,

ich habe ein kleines Programm, welches Kommandos per Ethernet bekommt, auswertet und darauf reagiert. Dabei wird der empfangene String an eine Funktion übergeben:

Loop (Ausschnitt)

while (client.connected()){
       if (client.available()){         
        char c = client.read();
        
        if (c == '\n' || c == '\r') {
            
            Serial.flush();
            Serial.println(cmdString);         
            //Resolve Command -> 
            result=resolveCmd(cmdString + "\r\n");                           
            client.println(result);
            client.flush();            
            cmdString="";       
        
        }     
       else {
        cmdString += c;          
        }   

        
      }//if client.available        
   }//while client connected 

Die zugehörige Funktion (Beispiel der Übergabe: "setport#13,1" => Port 13 auf high:

int resolveCmd(String cmdString) {
  
  // **********SETPORT************
  if (cmdString.startsWith("setport")) {
    String temp;
    temp=(cmdString.substring(cmdString.indexOf("#")+1,cmdString.indexOf(",")));
    int iPort=temp.toInt();
    temp=(cmdString.substring(cmdString.indexOf(",")+1,cmdString.indexOf(";") ));
    int iValue=temp.toInt();

    if (debug) {
      Serial.print("Port ");
      Serial.println(iPort);
      Serial.print("Value ");
      Serial.println(iValue);
    }
    digitalWrite(iPort,iValue); 
    
    return 1;
  }
}

Passt soweit und läuft. Aber: Ich wollte meinen Code etwas modularer erstellen, d.h. Funktionalitäten in externe Files bringen. Dazu habe ich eine fEthernet.h / cpp erstellt:

.h:

#pragma once

void fEthernet_setup();
void fEthernet_socket_server();
int resolveCmd(String cmdString);

.cpp

void fEthernet_socket_server()
{
int i= resolveCmd("Test")   
}

int resolveCmd(String cmdString)
{
}

Problem: egal was ich versuche, aber sobald ich Strings als Parameter beim Funktionsaufruf ausserhalb meiner *.ino verwende, meckert der Compiler. Innerhalb der eigentlichen ino läuft alles. Was könnte mein Fehler sein? Einen String innerhalb meiner Funktion zu Deklarieren funktioniert hingegen.
Nun habe ich ein wenig gegoogelt und gelesen, dass Strings auf dem Arduino keine gute Idee seien und man besser Character-Arrays nehmen sollte.

Frage: wie kann ich Methoden wie
-> startsWith
-> Substring + indexof

hier anwenden?

Danke für eure Hilfe!
VG
Steffen

#pragma once
#include <Arduino.h>

void fEthernet_setup();
void fEthernet_socket_server();
int resolveCmd(String cmdString);

Gar nicht, weil C-Strings keine Methoden haben!

Aber, es gibt reichlich Funktonen, welche mit c-Strings umgehen können.
https://www.nongnu.org/avr-libc/user-manual/group__avr__string.html
https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html
Und viele weitere.

Auf einem ESP sind Arduino-String Objekte durchaus gebräuchlich, und sollten in .cpp Dateien nach
#include <Arduino.h>
verwendbar sein.
Mir fällt außer dem fehlenden Semikolon nichts auf. Wie lautet denn die Fehlermeldung?

@Michael_x: du hast den entscheidenden Hinweis gegeben: das include der Arduino.h hatte ich zwar im cpp-File, nicht aber im Header. Da ich dort aber die Funktion "vorgestellt" habe, hat er mir das String angemeckert. Fehler:
int resolveCmd(String cmdString); -> 'String' was not declared in this scope + 'resolveCmd' cannot be used as a function.

Lösung:

#pragma once
#include <Arduino.h>. // <- hat gefehlt

void fEthernet_setup();
void fEthernet_socket_server();
int resolveCmd(String cmdString);

Keine Fehler... Super!
Aber: ist die Herangehensweise dennoch OK, oder ist die Verwendung von Strings nicht zu empfehlen (habe ich mal gelesen). Die Funktionen können durchaus 100x pro Tag aufgerufen werden. Mülle ich mir mit diesem Konzept den RAM zu, oder wird nach Verlassen der beiden Funktionen fEthernet_socket_server() und resolveCmd(String cmdString) der Speicher wieder freigegeben?

Tausend Dank für eure Hilfe!

VG
Steffen

Wenn Du den String außerhalb der Funktion anlegst, kann ihn die Funktion auch nicht freigeben, da er ihr ja "nicht gehört".

Schau mal in die Doku nach der Methode reserve(Länge).
Damit kannst Du dem String eine Mindestlänge geben, so dass der nicht laufend verlängert und damit umkopiert werden muss. Die Länge solltest Du an der zu erwartetenden Maximallänge fest machen

Gruß Tommy

Besser so:

int resolveCmd(String& cmdString)

Danke für den Tipp. So, wie das Codebeispiel ist, sollte es doch nur innerhalb der Funktion bleiben, oder?

@Serenifly : Was bewirkt das &? Kannst du mal einen Suchbegriff nennen, mit welchem ich mir mehr Infos erarbeiten kann?

VG
Steffen

Suchtipp: "C++ Parameter Übergabe per Referenz"

OK, danke...schaue ich mir mal an.

VG
Steffen

Funktionsparameter werden normalerweise innerhalb einer Funktion als Kopie angelegt. Mit Zeigern oder Referenzen kann die originale Variable verwendet werden. Änderungen innerhalb der Funktion wirken sich dann auch auf die originale Variable aus. Mit int resolveCmd(const String& cmdString) ist der String innerhalb der Funktion konstant schreibgeschützt.

EDIT nach #12.

Hmmm .....
Konstant nicht, aber schreibgeschützt, in der Funktion.
Es kann nicht garantiert werden dass der String unverändert bleibt, wenn man eine Referenz nutzt. Jeder Thread oder jede ISR, welche da rein grätscht, kann den String ändern.

Ich rate dazu, jedes mal "read only" zu denken, wenn man "const" liest.
Beachtenswert: Das const zeigt seine Wirkung nur im jeweiligen Geltungsbereich.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.