frage zu snprintf() und (uint8_t *)

hallo,

ich blicke leider nicht so ganz durch bei dem thema arrays, chars, strings usw....
hab schon den ganzen tag gegoogled.....

und zwar möchte ich ein sensortyp, sensorID, batteriespannung, und bis zu 4 messwerte senden via funk..
und später vielleicht auch von attiny zu attiny (via serial ? oder wie macht man das ?).

bisher hab ich folgendes:

char message[25];

char SensorTyp = 'A';
int SensorID = 1;
int temp = 88; //sensor ist noch nicht vorhanden

int VCC = readVcc();

snprintf(message, 25, "STyp=%d&SID=%d&VCC=%d&T=%d", SensorTyp, SensorID, VCC, temp) ;

softSerial.println(strlen(message)); // Stringlänge ermitteln
softSerial.println(message); // gesendeter String

vw_send((uint8_t*) message, strlen(message));
vw_wait_tx();

strlen() hab ich verstanden - ermittelt die stringlänge ?

unint8_t ist irgendwas in richtung unsigned char mit 8 bit ?
aber was haben die klammern und das sternchen zu bedeuten ?

und zu printf() und sprintf() hab ich was gefunden, aber zu snprintf, waren die informationen sehr rar.

snprintf(array-name, länge, "text", wert1, wert2, wert3, usw) ?

frage ist jetzt, wie lang darf das maximal sein ? ich bekomme nämlich ab einer bestimmten länge (so ab 26) probleme....
wie viel werte darf ich maximal da reinschreiben ?

das A kommt als ascii an, wie kann ich das wieder in ein char umwandeln ?

wenn ich von dem was entferne:
"STyp=%d&SID=%d&VCC=%d&T=%d"
zB:
"ST=%d&SID=%d&VCC=%d&T=%d"
funktioniert das programm auch nicht mehr ?

vielen dank !

*uint8_t = Zeiger auf unsigned char (Merke: Array Variablen sind Zeiger auf das erste Element)
() = Cast
Da wird einfach ein char Array auf ein Byte Array gecastet

snprintf() ist wie printf(), aber es schreibt das Ergebnis in einen Puffer. Das "n" bedeutet wie bei anderen String-Funktionen auch (siehe strncpy() oder strncat()), dass durch den zusätzlichen Längen-Parameter ein Puffer-Überlauf verhindert wird. Ich weiß aber nicht wo du suchst, dass du da nichts findest. Das ist eine Standard C Funktion:
http://www.cplusplus.com/reference/cstdio/snprintf/

In einen C-String darfst du Länge - 1 Zeichen reinschreiben. - 1 weil noch Platz für den Null-Terminator sein muss. Ein Haken mit den n-Funktionen ist, dass die Strings zwar abgeschnitten werden, aber dann nicht richtig terminiert sind. Das kann man beheben wenn man Länge - 1 als Parameter übergibt.

Dann noch eine Sache:
%d ist für Integer-Zahlen. Wenn du ein ASCII-Zeichen willst ist %c korrekt. Siehe printf() Doku:
http://www.cplusplus.com/reference/cstdio/printf/
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaa3b98c0d17b35642c0f3e4649092b9f1

vielen dank für die antwort !

und was bedeutet das in dem zusammenhang ?
"(uint8_t*) message"
(für laien (:wink:

gesucht hab ich ehr auf deutschsprachigen seiten, da ich auf deutsch schon schwierigkeiten hab, das nachzuvollziehen....

ok, das mit dem "-1" hab ich verstanden, aber gibts nach oben hin eine begrenzung ? oder ist das irgendwann zu viel für ein attiny85 ?

also so:
"STyp=%c&SID=%d&VCC=%d&T=%d"

ich habs jetzt mal zum ausprobieren vereinfacht:

hiermit:
snprintf(message, 26, "ST=%d&SID=%d&VCC=%d", SensorTyp, SensorID, VCC) ;

bekomme ich das raus:

"ST1&SID=1&VCC=3606"
also das "=" wird beim ST einfach weggelassen ???

gibts vielleicht irgendwo eine deutschsprachige seite mit einfachen übungen zu diesen themen ?
weil leider brauche ich das anscheinend, aber so ganz einfach wie den rest finde ich das nicht....

ich hab jetzt statt der sensortypnummer wieder ein char eingesetzt

hiermit:
snprintf(message, 26, "ST=%c&SID=%d&VCC=%d", SensorTyp, SensorID, VCC) ;

bekomme ich das raus:

"ST=A SID=1&VCC=3606"
also das "&" wird beim ST einfach weggelassen..........

Lies der den WP Artikel zu "Typumwandlung" durch. Dadurch wird erzwungen dass der Compiler ein char Array als unsigned char Array behandelt ohne zu meckern. Wobei dass der Macher der Library auch besser hätte lösen können (mit einem void Zeiger, dann hätte man beliebige Datentypen übergeben können)

Bei mir geht es:

void setup()
{
  Serial.begin(9600);

  char buffer[31];

  char SensorTyp = 'A';
  int SensorID = 15;
  int Vcc = 4999;
  int temp = 88;

  snprintf_P(buffer, sizeof(buffer), PSTR("STyp=%c&SID=%d&VCC=%d&T=%d"), SensorTyp, SensorID, Vcc, temp);
 Serial.println(strlen(buffer));
 Serial.println(buffer);
}

Den Puffer solltest du allerdings größer machen.
Die _P Version und das PSTR() sorgen dafür dass der Format String kein RAM belegt. Das ist aber mehr oder weniger Kosmetik. Das ändert an der Formatierung an sich nichts zu tun

"ST1&SID=1&VCC=3606"
also das "=" wird beim ST einfach weggelassen

Da hast du auch wieder %d statt %c drin. Wobei er da wie bei mir das = natürlich schreiben sollte, aber dann statt dem ASCII Zeichen selbst dessen Code schreibt (z.B. 65 statt 'A')

Nachtrag:
Sehe gerade dass snprintf() auch bei zu kurzem Puffer richtig terminiert. Dann verhält es sich doch anders als z.B. strncpy() :slight_smile:

super ! danke !

also mit _P und PSTR() ging ein bischen mehr, aber wenn ich jetzt noch eine weitere seriale ausgabe hinzufügen will, funktioniert das programm weider nicht........

kanns sein, das softwareserial zu viel ram belegt und der attiny damit überlastet ist ?

dann lass ich softserial einfach weg (;

jetzt bekomme ich den empfangenen string nicht "zerlegt" ):

ST=A&SID=1&VCC=3867&t=8888

// split data into array sensorData
sscanf(Message, "%c,%d,%d,%d", &ST[0], &SID[1], &VCC[2], &T[3]); // Converts a string to an array

char SensorType = ST[4];

Serial.print("SensorTyp = ");
Serial.println(ST);

int SensorID = SID[4];

Serial.print("SensorID = ");
Serial.println(SensorID);

ich glaube auch nicht, dass das ganz richtig ist ........

Ja, da wirst du ein RAM Problem haben. Davon gibt es auf einem ATtiny nicht viel. Generell überall wo du print()/println() mit String-Literalen verwendest das F() Makro benutzen:

Serial.println(F("String im Flash"));

Dann belegt der String kein RAM. Das wird dich hier aber auch nicht retten.

Wegen scanf():
Dein Format-String stimmt auch überhaupt nicht mit dem empfangenen String überein.

Korrekt:

char buffer[] = "ST=A&SID=1&VCC=3867&t=8888";

char sensorTyp;
int sensorID, Vcc, temp;

sscanf_P(buffer, PSTR("ST=%c&SID=%d&VCC=%d&t=%d"), &sensorTyp, &sensorID, &Vcc, &temp);

Und ohne unnötige Arrays

Allerdings ist es gerade auf einem ATtiny Unsinn hier scanf() zu verwenden. Es gibt keine anderen Funktionen die so viel Speicher fressen wie printf() und scanf(). Vor allem Flash, aber auch temporär RAM.

Diese Sachen kann man sehr einfach und ressourcenschonend mit strtok() + atoi() Parsen:

strtok(buffer, "=");
sensorTyp = *(strtok(NULL, "="));
sensorID = atoi(strtok(NULL, "="));
Vcc = atoi(strtok(NULL, "="));
temp = atoi(strtok(NULL, "="));

Oder vielleicht etwas sauberer, aber mit einer zusätzlichen (kleinen) Funktion:

sensorTyp = *(strchr(strtok(buffer, "&"), '=') + 1);
sensorID = atoi(strchr(strtok(NULL, "&"), '=') + 1);
Vcc = atoi(strchr(strtok(NULL, "&"), '=') + 1);
temp = atoi(strchr(strtok(NULL, "&"), '=') + 1);

Dein Flash und RAM werden es dir danken.

Es stellt sich aber die Frage wieso du nicht einfach die Werte nur mit Kommas abtrennst, sondern überflüssige Bezeichner mitsendest. Die Reihenfolge ist sowieso bekannt. Es ist nicht so, dass du nach den Bezeichnern suchst. Also einfach so machen:

char buffer[] = "A,1,3867,8888";

char sensorTyp;
int sensorID, Vcc, temp;

sensorTyp = *(strtok(buffer, ","));
sensorID = atoi(strtok(NULL, ","));
Vcc = atoi(strtok(NULL, ","));
temp = atoi(strtok(NULL, ","));

vielen dank !

bei strtok() bin ich dann auch "hängengeblieben", weil irgendwie "flexibler" ist.....

komm ich auch irgendwie besser mit klar....

das mit den bezeichnern kommt daher, weil ich ein sketch aus dem netzt verwendet hab, aber du hast eigentlich recht, die reihenfolge ist bekannt.....

allerdings gibt verschiedene sensortypen und von denen teilweise auch noch mehrere......
ich versuche jetzt das so umzuschreiben, das der empfänger automatisch erkennt, wie viele sensoren es gibt...
also das der empfänger sketch sofort für das maximum ausgelegt ist....

ma sehen, ob ich das hinbekomme (;

aber danke du hast mir wiedermal sehr geholfen !

ich hab mir überlegt den sensortyp nicht mit "A" zu bezeichnen sondern mit "TRXX" (Temperatur, rlf, platzhalter, platzhalter).

leider kommt mit dem hier.....:
char sensorTyp = 'TRXX';
snprintf_P(message, 27, PSTR("%c;%d;%d;%d;%d"), sensorTyp, sensorID, Vcc, temp, rlf) ;
........."X" an

und mit dem hier.......:
char sensorTyp[5] = {"TRXX"};
snprintf_P(message, 27, PSTR("%c;%d;%d;%d;%d"), sensorTyp, sensorID, Vcc, temp, rlf) ;
.......kommt "f" an.

Du musst dir wirklich mal absolute Grundlagen zu Zeichen und C Strings ansehen. Sonst stocherst du immer im Dunklen.

Dass dass Unsinn ist hast du schon selbst erkannt:

char sensorTyp = 'TRXX';

Strings werden aber so initialisiert:

char sensorTyp[5] = "TRXX";

oder

char sensorTyp[] = "TRXX";

Dann musst du auch deinen Parameter bei printf() anpassen!! printf() überprüft keine Datentypen. Das verlässt sich darauf dass du das richtig machst und produziert Mist wenn es nicht passt.

%c ist ein Zeichen. Für Strings gibt es %s

Und auch das Auslesen musst du dann etwas anders machen. strtok() geht genauso. Aber du kannst Strings (und Array allgemein) nicht mit = zuweisen wie ein Zeichen. Sondern du braucht eine Funktion aus der strcpy() Familie um den Teil-String den du mit strtok() bekommst in ein Array zu kopieren. Am besten strlcpy() (wie strncpy() aber terminiert bei Überlänge korrekt):
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#ga64bc119cf084d1ecfd95098994597f12

danke ! du warst schneller !

hatte das problem zeitgleich gelösst, aber so:

char *sensorTyp = "TRXX";
snprintf_P(message, 27, PSTR("%s;%d;%d;%d;%d"), sensorTyp, sensorID, Vcc, temp, rlf) ;

ist da was falsch dran ?

grundlagen zu arrays, strings, char usw fehlen mir wirklich........
hoffe das ich mir so "learning by doing" die sachen aneignen kann...
nur durchlesen bringt bei mir leider nicht wirklich was.....

das mit dem auslesen ist ein guter hinweis ! danke !

so:

char sensorTyp[5];
strlcpy (sensorTyp, (strtok(message, ";")), sizeof(message));

oder ist da was falsch dran ?

char ssensorTyp ist ein char array = string, richtig ?
und muss null-terminiert werden, deswegen [5]

magictrips:
char *sensorTyp = "TRXX";

Das geht weil Arrays eben nicht viel mehr als Zeiger auf das erste Element sind. Es gibt da ein paar subtile Unterschiede. Der Compiler behandelt Array Variablen etwas anders als reine Zeiger. Aber hier spielt es keine Rolle.
Falsch ist es nicht, aber normal deklariert man Strings eher mit Array Klammern [] und lässt den Compiler die Größe bestimmen.

strlcpy(sensorTyp, (strtok(message, ";")), sizeof(message));

Ja. Oder wenn es für dich klarer ist, mach zwei Zeilen draus:

char* ptr = strtok(message, ";");
strlcpy(sensorTyp, ptr, sizeof(message));

Dabei wird deutlich, dass strtok() einen Zeiger auf den Anfang des Tokens liefert (ähnlich wie wie viele andere String Such-Funktionen wie strchr() oder strstr()). Ist aber Geschmackssache.

char ssensorTyp ist ein char array = string, richtig ?

Genaugenommen sind C Strings Null-terminierte char Arrays. Ein char Array muss nicht zwangsläufig ein String sein. Mann kann da auch Zahlen drin speichern. char wird oft mit ASCII Zeichen gleichgesetzt, aber es ist eigentlich nur ein vorzeichenbehafteter 8-Bit Wert.