Forum: PC-Programmierung Lebensdauer von lokalen Variablen


von Christian J. (Gast)


Lesenswert?

Hallo,

aus der leidigen Erfahrung eines "Stackoverflow" heraus bei den winzigen 
2k eines AVR328 muss nun optimiert werden. Stringverarbeitung hat es 
leider in sich, auch sprintf nimmt sich einen fetten Batzen des Stacks.

Versuche mit malloc und calloc waren fruchtlos, nach 4-5 Durchläufen kam 
nur noch ein NULL Zeiger zurück, obwohl alles ge-free()'t wurde. Keine 
Ahnung wieso, beim stm32 lief das alles immer prima. ich weiss aber auch 
nicht wie gross der Heap beim Arduino eingestellt worden ist.

Annahme:

void nixgut() {
  int a,b,c;
  (.....)
  {
     int e,f,g;

  }
  (weiter im Text)
}

a,b,c, liegen in einer inneren Klammer und sind außerhalb dieser nicht 
sichtbar. Aber reduziert sich damit auch wirklich der Stack, damit das 
Nachfolgende mehr Platz hat? Ich versuche alles nur solange leben zu 
lassen wie es gebraucht wird und keine Variable mehr am Anfang zu 
deklarieren.

Christian

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Versuche mit malloc und calloc waren fruchtlos

Die machen es eher schlimmer. Der Verwaltungsoverhead und die ggf. 
suboptimale Aufteilung erhöhen den Bedarf.

Christian J. schrieb:
> beim stm32 lief das alles immer prima.

Die haben ja auch viel mehr Speicher...

Christian J. schrieb:
> Aber reduziert sich damit auch wirklich der Stack, damit das
> Nachfolgende mehr Platz hat?
Nein. Compiler optimieren Funktionsaufrufe meist so, dass einmal zu 
Beginn alles benötigte allokiert wird. Es werden aber durchaus 
Stack-Plätze wiederverwendet, falls möglich.

Schau dir doch einfach den erzeugen Assembler-Code an, der räumte alle 
Zweifel aus. Und zeig mal den ganzen Code, die paar Integer-Variablen 
sind überhaupt kein Problem.

von Oliver S. (oliverso)


Lesenswert?

Niklas G. schrieb:
> Nein. Compiler optimieren Funktionsaufrufe meist so, dass einmal zu
> Beginn alles benötigte allokiert wird.

Oder auch nicht.
Vielleicht hält der Compiler die auch gar nicht auf dem Stack, sondern 
nur in Registern. Da ist alles möglich.

Oliver

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Daher eben den Assembler-Code anschauen und sich das Raten ersparen.

von Christian J. (Gast)


Lesenswert?

Niklas G. schrieb:
> Daher eben den Assembler-Code anschauen und sich das Raten ersparen.

Geht leider nicht in der Arduino IDE und selbst bei EmBitz ist das ein 
Problem, was nicht ohne Skripte lösbar ist :-(

Hätte ja sein können, dass die Klammern eben ein Abbauen des Stack Top 
verursachen.

Der Code soll so schonend wie möglich für den Speicher geschrieben 
werden.
1
/* Sende eine sms an das Ziel mit Text
2
  Mode FLASH = Text ist im Flash zu finden
3
       RAM   = Text wird im RAM übergeben
4
     
5
  AT+CMGS="+85291234567"<CR>
6
  <CR><LF>><Space>     
7
     
8
*/
9
int SIM800_SendSMS(const char* text,int mode)
10
{
11
  char MsgToSend[160];
12
    
13
  /* MsgToSend = MsgStatusLine + MsgContent */
14
  
15
  /* Zeitstempel als Erstes vorbauen */
16
  if (flags.TimeStampIsValid ) {
17
    char signal[6];
18
    switch (CSQuality) {
19
      case 1: strcpy(signal,"-");   break;
20
      case 2: strcpy(signal,"+");    break;
21
      case 3: strcpy(signal,"++");  break;
22
      case 4: strcpy(signal,"+++");  break;
23
      default: strcpy(signal,"NA");  break;
24
    }
25
26
    /* Low Power oder Full Mode */
27
    char lowpower[6];
28
    if (flags.LowPowerMode)  strcpy(lowpower,"Eco");
29
    else           strcpy(lowpower,"Full");
30
31
    // /* LEDs ein oder aus */
32
    char leds[8];
33
     if (flags.ShowLED)    strcpy(leds,"LED");
34
     else           strcpy(leds,"NoLED");
35
36
    sprintf(MsgToSend,"%02u.%02u, %02u:%02u, %s, %s, %02u%%, %s\r\n", 
37
            timestamp.day, timestamp.month, timestamp.hour,
38
            timestamp.min, lowpower, leds, BattProzent,  signal);
39
  } else
40
    sprintf(MsgToSend,"--.--.--, --:--, %02u%%, ---\r\n",BattProzent);
41
42
  /* MsgContent existiert nur zwischen diesen {} */
43
  {
44
    /* SMS Text aus Progmem oder RAM lesen und verketten */
45
    char MsgContent[MAX_MSG_LENGTH+2];
46
    if (mode == FLASH)
47
      ReadFromFlash(MsgContent,text);
48
    else {
49
      if (strlen(text) > sizeof(MsgToSend)) {
50
        debugln(F("Text zu lang!"));
51
        return 0;
52
      }
53
      strcpy(MsgContent,text);
54
    }
55
    
56
    /* Hauptstring zusammen fügen */
57
    strcat(MsgToSend,MsgContent);
58
  }
59
60
  /* In MsgToSend steht jetzt, was gesendet werden soll, entweder ein
61
    String aus dem RAM oder ein indirekter Inhalt aus dem Flash */
62
  
63
  debugln(F("----------------- SMS ---------------------"));
64
  debugln(MsgToSend);
65
  debugln(F("-------------------------------------------"));
66
67
  /* Pending Alarme löschen, da False Alarms möglich */
68
  cli();
69
  flags.RadarAlarmHold = false;
70
  /* GSM bleibt an, alle Timer zurücksetzen */
71
  ResetAllWDTTimers();
72
  sei();
73
  
74
#ifdef SEND_SMS
75
  /* Empfangsbereitschaft des GSM prüfen */
76
  if (!SIM800_SendAT("AT\r\n","OK"))
77
    return 0;
78
79
  /* Text Mode sms */
80
  SIM800_SendAT("AT+CMGF=1\r\n","OK");
81
  
82
  /*AT Command und Zielnummer senden */
83
  char atcmd[40];
84
  strcpy(atcmd,"AT+CMGS=\"");
85
  strcat(atcmd,admin_number);
86
  strcat(atcmd,"\"\r\n");
87
  serialSIM800.print(atcmd);
88
  serialSIM800.flush();
89
  
90
  /* Warte bis "\r\n> " empfangen wird */
91
  SIM800_GetChars(atcmd);
92
  if (!(strstr(atcmd,"\r\n>")))
93
    return 0;
94
  
95
  /* Cntrl-Z als Schlusszeichen */
96
  serialSIM800.println(char(26));
97
98
  /*Bis zu 60s kann es dauern, bis mit CMGS:<zeichenzahl> geantwortet wird */
99
  while (1) {
100
    SIM800_GetChars(atcmd);
101
    if (strstr(atcmd,"GMGS")))
102
      return 1;
103
  }
104
#endif
105
106
  return 1;
107
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Geht leider nicht in der Arduino IDE

Kann man sehr wohl:
Beitrag "Re: Auf Arduino erstelltes Programm in C und Assembler anschauen"


Christian J. schrieb:
> Der Code soll so schonend wie möglich für den Speicher geschrieben
> werden.

Jaa.

Christian J. schrieb:
> char MsgToSend[160];

Christian J. schrieb:
> char signal[6];

Christian J. schrieb:
> char atcmd[40];

Du kannst auch berechnete kurze Strings oder einzelne Zeichen sofort 
absenden, statt sie erst komplett zwischenzuspeichern. Du kannst statt 
sprintf itoa und strcpy nutzen. Du kannst den Code auf einzelne 
Funktionen aufteilen, die nacheinander aufgerufen werden. Du kannst die 
Puffer ggf. global allokieren damit der Linker den Speicherverbrauch 
berechnet.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Niklas G. schrieb:
> Du kannst den Code auf einzelne
> Funktionen aufteilen, die nacheinander aufgerufen werden.

Das habe ich schon gemacht. Eine Funktion berechnet die Strings, ein 
Flag, Die nächste nimmt das Flag und arbeitet werden, damit keine 
Verschachtelungen
entstehen. Und ja, das GSM Modul wartet solange bis alles da ist nach 
dem ">". Das liesse sich auch noch ausnutzen. Global ist die letzte 
Lösungh, wenn alles nicht geht. Ein Puffer für alles, sind ja ein paar 
Module, die sowas machen. Aber nie alle gleichzeitig.

Am liebsten wäre ja calloc (strlen(....))) gewesen aber das haute gar 
nicht hin, 2 Std für die Katz. Ich lese mir das nachher mal durch, 
scheint ne größere Baustelle zu sein. Beim stm32 habe ich mixed Code im 
Debug Fenster, da ist da sehr schön zu sehen und vor allem  per JTAG und 
nicht Bootloader...


Weisst du vielleicht auch warum man mit strstr nicht nach dem "+" 
Zeichen suchen kann? Er nimmt alles, nur das nicht.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Naja, global weiss man wenigstens wo man liegt. Es wird knapp :-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Niklas G. schrieb:
> Du kannst auch berechnete kurze Strings oder einzelne Zeichen sofort
> absenden, statt sie erst komplett zwischenzuspeichern.

Er könnte zumindest Pointer verwenden. Es gibt keinen Grund dafür, die 
String-Konstanten erst in einen Buffer zu kopieren.

Beispiel:
1
const char * signal;
2
3
switch (CSQuality) {
4
      case 1: signal = "-";   break;
5
      case 2: signal = "+";    break;
6
      case 3: signal = "++";  break;
7
      case 4: signal = "+++";  break;
8
      default: signal = "NA";  break;
9
    }

> Du kannst statt sprintf itoa und strcpy nutzen.

Jepp. Das hier:
1
sprintf(MsgToSend,"%02u.%02u, %02u:%02u, %s, %s, %02u%%, %s\r\n", 
2
            timestamp.day, timestamp.month, timestamp.hour,
3
            timestamp.min, lowpower, leds, BattProzent,  signal);

könnte man so umschreiben.

Aber auch das hier:
1
  {
2
    /* SMS Text aus Progmem oder RAM lesen und verketten */
3
    char MsgContent[MAX_MSG_LENGTH+2];
4
    if (mode == FLASH)
5
      ReadFromFlash(MsgContent,text);
6
    else {
7
      if (strlen(text) > sizeof(MsgToSend)) {
8
        debugln(F("Text zu lang!"));
9
        return 0;
10
      }
11
      strcpy(MsgContent,text);
12
    }
13
    
14
    /* Hauptstring zusammen fügen */
15
    strcat(MsgToSend,MsgContent);
16
  }

schmeisst nur mit RAM so um sich. Da muss überhaupt nichts erst in ein 
Array kopiert werden.

Könnte man zum Beispiel so machen:
1
    /* SMS Text aus Progmem oder RAM lesen und verketten */
2
3
    if (mode == FLASH)
4
      ReadFromFlash(MsgToSend + strlen (MsgToSend),text);
5
    else {
6
      if (strlen(text) > sizeof(MsgToSend)) {
7
        debugln(F("Text zu lang!"));
8
        return 0;
9
      }
10
      strcat(MsgToSend,text);
11
    }

Den Kunstgriff mit den umschließenden Klammern kann man hier direkt 
vermeiden und man braucht kein Array. Zudem ist der Code kürzer.

Obwohl ich nicht ganz diese Prüfung auf die Länge verstehe. Der TO 
arbeitet hier mit strcat(), setzt also zwei Strings zusammen. Er prüft 
aber nur auf die Länge von text. Das reicht aber nicht. Es müsste 
heißen:
1
      if (strlen(text) + strlen(MsgToSend) >= sizeof(MsgToSend)) {

Beachte auch ">=" statt dem falschen ">".

Warum diese Prüfung überhaupt gar nicht gemacht wird, wenn der Text aus 
dem Flash kommt, ist mir auch schleierhaft.

: Bearbeitet durch Moderator
von Christian J. (Gast)


Lesenswert?

Frank M. schrieb:
> Er könnte zumindest Pointer verwenden. Es gibt keinen Grund dafür, die
> String-Konstanten erst in einen Buffer zu kopieren.

Das erzeugt genau 34 Bytes mehr "globale Variablen" und reduziert den 
Platz für lokale um 34 Bytes.

Aber ansonsten setze ich deine guten Ideen grad mal um.... man fängt ja 
erst an zu sparen, wenn es eng wird. Die Funktion ist ja erstmal da.

von Rolf M. (rmagnus)


Lesenswert?

Christian J. schrieb:
> if (flags.ShowLED)    strcpy(leds,"LED");
>      else           strcpy(leds,"NoLED");

Dir ist klar, dass hier die Strings "LED" und "NoLED" beide erstmal vom 
Flash in den RAM kopiert und dann je nach Ergebnis der if-Abfrage einer 
davon per strcpy noch ein zweites mal im RAM kopiert wird?
Lies dich mal darüber ein, wie man Stringliterale im Flash benutzt und 
was strcpy_P macht.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Rolf M. schrieb:
> Dir ist klar, dass hier der String "NoLED" erst vom Flash in den RAM
> kopiert und dann per strcpy noch ein zweites mal im RAM kopiert wird?

Ja... weiss ich .... irgendwo muss es ja liegen und das kann nur im 
Flash sein und bei der Init Sequenz wird es zugewiesen. Das sind 
ellenlange Sequenzen bei jeder Funktion am Anfang.

von Falk B. (falk)


Lesenswert?

Christian J. schrieb:
> iklas G. schrieb:
>> Daher eben den Assembler-Code anschauen und sich das Raten ersparen.
>
> Geht leider nicht in der Arduino IDE und selbst bei EmBitz ist das ein
> Problem, was nicht ohne Skripte lösbar ist :-(
>
> Hätte ja sein können, dass die Klammern eben ein Abbauen des Stack Top
> verursachen.
>
> Der Code soll so schonend wie möglich für den Speicher geschrieben
> werden.

Dann sollte man sich mal mit dem Thema PROGMEM vertraut machen, beim 
Arduino ist das die Funktion F(). Damit werden konstante Strings in den 
Flash gepackt. Im jetzigen Zustand verschwenden all deine konstanten 
Strings viel RAM. Klingt komisch, ist aber so.

https://www.arduino.cc/reference/en/language/variables/utilities/progmem/

von Rolf M. (rmagnus)


Lesenswert?

Christian J. schrieb:
> Rolf M. schrieb:
>> Dir ist klar, dass hier der String "NoLED" erst vom Flash in den RAM
>> kopiert und dann per strcpy noch ein zweites mal im RAM kopiert wird?
>
> Ja... weiss ich .... irgendwo muss es ja liegen und das kann nur im
> Flash sein und bei der Init Sequenz wird es zugewiesen.

Es reicht aber, wenn es einmal im Flash liegt und nicht zusätzlich dazu 
noch einmal im ja eh schon so knappen RAM.

von Christian J. (Gast)


Lesenswert?

Naja, kenne ich schon, sämtliche Nachrichten liegen da auch und werden 
erst geholt, wenn sie gebraucht werden. Aber so ganz bin ich da noch 
nicht durch, geht vielleicht noch besser.
1
const char flash_sysdown[]      PROGMEM = "Shutdown!";
2
const char flash_alarm_off[]    PROGMEM = "Alarm ist jetzt AUS!";
3
const char flash_alarm_on[]     PROGMEM = "Alarm ist jetzt scharf!";
4
const char flash_lowpower_on[]  PROGMEM = "Lowpower Mode aktiv!";
5
const char flash_lowpower_off[] PROGMEM = "Fullpower Mode aktiv!";
6
7
....
8
9
/* Kopiert einen Text aus dem Flash ins Ram */
10
void ReadFromFlash(char* target, const char* source)
11
{
12
  for (uint16_t k = 0; k < strlen_P(source);k++) {
13
    *(target + k)    = pgm_read_byte_near(source + k);
14
    *(target + k + 1) = '\0';  /* NUL Terminieren */
15
  }
16
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Aber so ganz bin ich da noch nicht durch, geht vielleicht noch besser.

Statt die Strings aus dem Flash ins Ram zu kopieren, um sie dann 
irgendwo hin auszugeben, könntest Du auch eine Variante Deiner 
Ausgabefunktion bauen, der direkt ein String aus dem Flash übergeben 
wird.

von Christian J. (Gast)


Lesenswert?

Frank M. schrieb:
> if (strlen(text) + strlen(MsgToSend) >= sizeof(MsgToSend)) {

Danke für den Hinweis auf den Fehler!

von Rolf M. (rmagnus)


Lesenswert?

Probier mal statt:
1
strcpy(leds,"NoLED");

dass hier:
1
strcpy_P(leds,PSTR("NoLED"));

PS: Oder was Rufus schreibt.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Statt die Strings aus dem Flash ins Ram zu kopieren, um sie dann
> irgendwo hin auszugeben, könntest Du auch eine Variante Deiner
> Ausgabefunktion bauen, der direkt ein String aus dem Flash übergeben
> wird.

Das Schleudern fing erst an als ich um meine Bastelei aufzubohren anfing 
Flash Strings und sprintf dynamische Strings zu sammen zu bauen. Ab da 
wurde es immer unübersichtlicher und ich habe diesen Monat bereits 50 
Euro nur an sms kosten verbraten auf der Pay Card, weil das kaum 
simulierbar ist. Wird Zeit ne flat zu bestellen.....

von Christian J. (Gast)


Lesenswert?

strcpy_P(leds,PSTR("NoLED"));

---->

Der Sketch verwendet 15114 Bytes (49%) des Programmspeicherplatzes. Das 
Maximum sind 30720 Bytes. Globale Variablen verwenden 1397 Bytes (68%) 
des dynamischen Speichers, 651 Bytes für lokale Variablen verbleiben. 
Das Maximum sind 2048 Bytes.

vs

Der Sketch verwendet 15096 Bytes (49%) des Programmspeicherplatzes. Das 
Maximum sind 30720 Bytes.
Globale Variablen verwenden 1403 Bytes (68%) des dynamischen Speichers, 
645 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

Spart also die 6 Bytes RAM ein!

Junge, Junge, ihr habts echt drauf.... !

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Junge, Junge, ihr habts echt drauf.... !

Ungefähr so? "Jungs und Mädels, hier habt ihr ein paar Zeilen meines 
Programms. Es funktioniert nicht, weil das Grundkonzept im Umgang mit 
Speicher von A bin Z vergurkt ist und jede Korrektur wie beim Mikado 
endet. Löst mein Problem, aber dalli, sonst setzt es Spott."

Wenn man merkt, dass das Pferd tot ist, sollte man absteigen (*). 
Passiert öfter, dass man nochmal ans Reissbrett muss.

*: Für Freunde der Bürokratie, eine nette Sammlung alternativer 
Lösungsvorschläge: http://www.roland-schaefer.de/totespferd.htm

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Löst mein Problem, aber dalli, sonst setzt es Spott."

Ich befasse mich seit 4 Wochen mit Arduino.... vorher habe ich damit 
nichts zu tun gehabt. Das nur dazu.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Ich befasse mich seit 4 Wochen mit Arduino.... vorher habe ich damit
> nichts zu tun gehabt. Das nur dazu.

Kein Problem damit. Aber ob es zur Lösung beträgt, in dieser Lage die 
bisherigen Hilfesteller zu verspotten?

Mein Tipp mit dem Reissbrett war ernst gemeint. So lange du kein Konzept 
zustande bringst, das radikal RAM einspart, wird das bestenfalls 
Flickwerk.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Spart also die 6 Bytes RAM ein!

Du hast ja nicht alles umgesetzt, ich habe Dir beschrieben, wie Du 
dieses hier komplett einsparen kannst:
1
char MsgContent[MAX_MSG_LENGTH+2];

Das sind weitaus mehr als 6 Bytes, die erst mal auf dem Stack angelegt 
werden wollen!

Desweiteren kannst Du auch dieses hier
1
 char MsgToSend[160];

komplett einsparen, wenn Du die Strings und numerischen Werte sofort 
rauspustest, statt sie erstmal in diesem Array zu sammeln.

Das sind schon mal 160 + 162 = 322 Bytes weniger auf dem Stack.

> Junge, Junge, ihr habts echt drauf.... !

Setze doch erstmal alles um und präsentiere Deinen korrigierten Code, 
bevor Du ein Urteil fällst.

von (prx) A. K. (prx)


Lesenswert?

Vielleicht nützt das was, vielleicht nicht: Seit vielen Jahren gibts im 
AVR Compiler auch managed pointer, ich weiss nur nicht mehr wie die da 
heissen. Sind Pointer, die auf ROM und RAM zeigen können und zur 
Laufzeit anhand des Wertes entsprechend drauf zugreifen. Etwas 
langsamer, etwas mehr Code, aber weniger nervend.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Auch das hier:
1
  char atcmd[40];
2
  strcpy(atcmd,"AT+CMGS=\"");
3
  strcat(atcmd,admin_number);
4
  strcat(atcmd,"\"\r\n");
5
  serialSIM800.print(atcmd);
6
  serialSIM800.flush();

kann man so schreiben:
1
  serialSIM800.print("AT+CMGS=\"");
2
  serialSIM800.print(admin_number);
3
  serialSIM800.print("\"\r\n");
4
  serialSIM800.flush();

Und schon wieder 40 Bytes eingespart.

Und komm mir jetzt nicht mit dem Output vom Linke/Compiler. Ich glaube 
nicht, dass der Arrays, die auf dem Stack liegen, überhaupt mitzählt.

Da gibt es noch jede Menge mehr dieser Codeoptimierungen, die gemacht 
werden könnten. Ich werde Dir aber jetzt nicht alle Stellen vorlesen. Du 
musst obiges nur auf die anderen Stellen genauso übertragen.

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Christian J. schrieb:
> strcpy_P(leds,PSTR("NoLED"));
>
> ---->
>
> Der Sketch verwendet 15114 Bytes (49%) des Programmspeicherplatzes.

Falscher Ansatz, statt die Strings in lowpower[], leds[] zu sammeln, 
kannst Du auch hier direkt die Strings ausgeben.

von Oliver S. (oliverso)


Lesenswert?

A. K. schrieb:
> Vielleicht nützt das was, vielleicht nicht: Seit vielen Jahren
> gibts im
> AVR Compiler auch managed pointer, ich weiss nur nicht mehr wie die da
> heissen. Sind Pointer, die auf ROM und RAM zeigen können und zur
> Laufzeit anhand des Wertes entsprechend drauf zugreifen. Etwas
> langsamer, etwas mehr Code, aber weniger nervend.

__memx

Oliver

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Zwischenmeldung,

aktuell sind über 300 Bytes eingespart worden an RAM durch Die 
Ratschläge oben. dauert aber alles etwas...... da ist viel schief 
gelaufen.....

Ich glaube  nicht, dass der Arrays, die auf dem Stack liegen, überhaupt 
mitzählt.

Nein, tut er nicht.... daher ja die Abstürze.....

von Rolf M. (rmagnus)


Lesenswert?

Christian J. schrieb:
> Spart also die 6 Bytes RAM ein!

Du musst das natürlich nicht nur mit einem String machen, sondern mit 
allen!

> Junge, Junge, ihr habts echt drauf.... !

Findest du es ok, andere für ihre Hilfe auch noch blöd anzumachen, statt 
zu versuchen, es zu verstehen?

Christian J. schrieb:
> Ich glaube  nicht, dass der Arrays, die auf dem Stack liegen, überhaupt
> mitzählt.
>
> Nein, tut er nicht.... daher ja die Abstürze.....

Natürlich nicht. Der Stack ist keine globale Variable. Die Arrays 
existieren zur Compilezeit noch nicht.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Findest du es ok, andere für ihre Hilfe auch noch blöd anzumachen, statt
zu versuchen, es zu verstehen?

Es scheint unmöglich zu sein, sich hier normal zu unterhalten, ohne dass 
früher oder später irgendjemand kommt, der irgendwas falsch auffasst und 
dann in den persönlichen Angriff übergeht. Das gilt auch für Mods. Das 
war ein Lob, aber egal.

von Rolf M. (rmagnus)


Lesenswert?

Christian J. schrieb:
> Das gilt auch für Mods. Das war ein Lob, aber egal.

Der letzte Satz liest sich halt mal überhaupt nicht so, mit dem "Junge, 
Junge" und den drei Punkten. Aber wenn's nicht bös gemeint war, dann ist 
ja alles gut.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Hier haut das nicht hin mit dem Makro, gibt nur Müll auf der 
seriellen.... nehme ich das PSTR weg ist alles wieder gut.

  /* Setze 9600 baud */
  SIM800_SendAT(PSTR("AT+IPR=9600\r\n"),"OK");
  /* Meldungen abschalten */
  SIM800_SendAT(PSTR("AT+CCWA=0\r\n"),"OK");
  /* Echo abschalten */
  SIM800_SendAT(PSTR("ATE0\r\n"),"OK");

da ich gut 100 dieser Sequenzen haben sind das die größten Ram Fresser.

von Rolf M. (rmagnus)


Lesenswert?

Christian J. schrieb:
> Hier haut das nicht hin mit dem Makro, gibt nur Müll auf der
> seriellen.... nehme ich das PSTR weg ist alles wieder gut.

Du darfst das PSTR nur mit Funktionen verwenden, die den String auch im 
Flash erwarten. Deshalb musst du ja auch strcpy_P statt strcpy 
verwenden.

von Christian J. (Gast)


Lesenswert?

Rolf M. schrieb:
> Du darfst das PSTR nur mit Funktionen verwenden, die den String auch im
> Flash erwarten. Deshalb musst du ja auch strcpy_P statt strcpy
> verwenden.

Ok, danke! Dann werde ich mir dafür mal ein Makro schreiben oder es 
sonst wie kapseln..... mir qualmt die Birne aber jetzt, erstmal vor die 
Türe gehen.....

SIM800_SendAT("AT+CSCLK=0\r\n","OK");

Denke mal eine Kapselung wäre das beste, das sind über 120 wie ich grad 
gezählt habe von diesen Aufrufen...

von Christian J. (Gast)


Lesenswert?

Sag mal Meister,

baue ich jetzt kompletten Blödsinn? Bin schon etwas erschöpft aber noch 
nicht fertig eben.... möchte diese konstanten Arrays weg haben und daher 
doch wieder auf malloc. Gibt zwei Sorten von SendAT, jene die konstant 
sind aber auch welche mit Parameter, die errehcnet werden. daher zwei 
Routinen jetzt. Läuft bisher....
1
int SIM800_SendFlashAT(const char* atc,const char* token)
2
{
3
  char res[128];
4
  char *kopie;
5
  
6
  /* Den Flash String ins Ram holen */
7
  kopie = (char*)malloc(strlen_P(atc)+1 * sizeof(char);
8
  strcpy_P(kopie,atc); 
9
  
10
  /* SIM800 Serial Buffer leeren */
11
  while (serialSIM800.available())
12
    serialSIM800.read();
13
14
  /* Sende AT Command */    
15
  serialSIM800.print(kopie);
16
  serialSIM800.flush();
17
  free(kopie);
18
  
19
  SIM800_GetChars(res);
20
21
  //debug(atc);
22
  // debug(res);
23
24
   if (strstr(res, token))    /* OK pruefen */
25
    return 1;
26
    
27
   return 0;
28
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Wie gesagt macht "malloc" verbrauchstechnisch alles schlimmer (und 
langsamer). Das solltest du komplett vermeiden! Das Problem an dieser 
Stelle ist ja nur, dass Arduino.print einen RAM-String braucht. Es soll 
aber angeblich auch einfach so gehen:
1
serialSIM800.print(F(atc));
Erspart die Kopie komplett.

sizeof(char) ist übrigens immer 1, denn die Einheit, in welcher sizeof 
die Größe angibt, ist in Vielfachen von char. Und da char immer genau 1 
char groß ist, ist sizeof(char)=1. Gilt analog auf für signed char und 
unsigned char.

von Christian J. (Gast)


Lesenswert?

Ok, das mit dem char weiss ich, ich schreibe auch if (a==true) statt nur 
das a, damit es leslicher ist.

Jut, so langsam rieselt der Kalk und morgen gehts weiter im Text, heute 
ist Ende Gelände, Festplatte voll nach 7h vor der Kiste.

Danke nochmal an alle für die wertvolle Hilfe!

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Ok, das mit dem char weiss ich, ich schreibe auch if (a==true) statt nur
> das a, damit es leslicher ist.

Jeder C++-Programmierer weiß aber dass das so ist, stolpert über den 
unnötig umständlichen Code und fragt sich ob da noch mehr 
"falsch"/unnötig ist...

von Christian J. (Gast)


Lesenswert?

Niklas G. schrieb:
> serialSIM800.print(F(atc));

Lässt sich nicht kompilieren.... angeblich ist macro F dafür nicht 
geeignet. Hmmm

K:\ArduinoSoftware\radar_gsm_wachhund\simctrl.ino:260:21: note: in 
expansion of macro 'F'

  serialSIM800.print(F(atc));

74: error: array must be initialized with a brace-enclosed initializer

Das aber schon:

serialSIM800.print(F("Hello"));

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Hm, versuch's mal so:
1
serialSIM800.print(reinterpret_cast<const __FlashStringHelper *> (atc));

Kenne mich mit Arduino auch nicht so gut aus. Das sind so die Gründe 
warum ich ARM bevorzuge :-)

von Christian J. (Gast)


Lesenswert?

Niklas G. schrieb:
> Kenne mich mit Arduino auch nicht so gut aus. Das sind so die Gründe
> warum ich ARM bevorzuge :-)

Ach.... das ist aber eine Neuigkeit... :-)))) Ich prügel mich damit auch 
nur herum, weil der F103 auf dem Bluepill Board zu gross ist....

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Ich prügel mich damit auch
> nur herum, weil der F103 auf dem Bluepill Board zu gross ist....

Wie, zu groß? Die gibt's in allen Größen...

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Es kompiliert mit dem cast.... und spielt auch!

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Apropos ARM, mit dem 407 habe ich vor 2 Jahren mal was gemacht... plus 3 
weitere Displayanzeigen im Haus... , alles DIY :-) Nur dem EEPROM, da 
hat nie hingenauen mit der eingebauten Technik. Jetzt Software Ic2. Hab 
da Tage mit verbracht :-(

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Jetzt Software Ic2.

Ja das Hardware-I²C der alten STM32 (inkl. F407) ist ziemlich vermurkst. 
EEPROM braucht man doch eigentlich nicht, man kann auch den internen 
Flash beschreiben, man muss sich aber eine Art Wear-Leveling basteln. Da 
gibt's aber sogar eine Bibliothek von ST für

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> Versuche mit malloc und calloc waren fruchtlos

Weil damit das Problem erst recht verschlimmert wird.
Ohne malloc wachsen beim AVR-GCC die Variablen von unten nach oben und 
der Stack von oben nach unten. Wenn es dann kracht, dann geht es 
garantiert nicht oder man war zu verschwenderisch mit Arraygrößen.

Mit malloc hat man dagegen immer die Arschkarte gezogen, denn es muß 
dafür extra Speicher reserviert werden. Das ist dann quasi, wie eine 
Festplatte partitionieren. Wie man es auch macht, man macht es falsch.
Malloc geht nur dann gut, wenn viel mehr als reichlich RAM verfügbar 
ist.

Auf nem MC darf man RAM nicht mit der Schöpfkelle austeilen. Man sollte 
ungefähr abschätzen, wieviel RAM ein Array auch wirklich benötigt.

Was man sich auf MCs unbedingt verkneifen sollte, sind unnütze 
Umkopierorgien. Viele Variablen braucht man nach der Verarbeitung eh 
nicht mehr, d.h. Umkopieren hat keinerlei Nutzen.

: Bearbeitet durch User
von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Niklas G. schrieb:
> Ja das Hardware-I²C der alten STM32 (inkl. F407) ist ziemlich vermurkst.
> EEPROM braucht man doch eigentlich nicht, man kann auch den internen
> Flash beschreiben, man muss sich aber eine Art Wear-Leveling basteln. Da
> gibt's aber sogar eine Bibliothek von ST für

Sicher, mag sein aber damit wollte ich mich nicht befassen. Habe das 
BKPRAM benutzt und ne Kopfzelle drauf gelötet auf das Board. Hängt seit 
2 Jahren an der Wand und läuft und läuft, nur die Sommerzeit Umstellung 
habe ich nicht hingekriegt, rechne mit Unixtime und UTC, da das 
einfacher ist.

Ich habe hier mal die für den 103 angehängt, die läuft aber, hat ewig 
gedauert. Nur so alle 3-4 Wochen hängt es sich mal auf. Strom weg und 
neu starten und alles wieder gut.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Christian J. schrieb:
> for (uint16_t k = 0; k < strlen_P(source);k++) {
>     *(target + k)    = pgm_read_byte_near(source + k);
>     *(target + k + 1) = '\0';  /* NUL Terminieren */
>   }

Terminieren muss man nur einmal, nicht nach jedem Zeichen. Zum zweiten 
kann die Offset-Berechnung mit k mehr kosten als ein simples 
Incrementieren des Pointers. Außerdem wird hier für jeden(!) 
Schleifendurchlauf die String-Länge von source ermittel. Das kostet 
unnötige CPU-Zeit - abgesehen von mehr Code durch die Offset-Berechnung.

Dieses sollte schneller gehen und auch weniger Code kosten:
1
  unsigned char ch;
2
3
  while ((ch = pgm_read_byte_near(source)) != '\0')
4
  {
5
    *target++ = ch;
6
    source++;
7
  }
8
9
  *target = '\0';

Warum Du dafür aber eine eigene Funktion schreibst, ist mir 
schleierhaft. Letztendlich macht diese Funktion nichts anderes als 
strcpy_P().

: Bearbeitet durch Moderator
von Christian J. (Gast)


Lesenswert?

Peter D. schrieb:
> Mit malloc hat man dagegen immer die Arschkarte gezogen, denn es muß
> dafür extra Speicher reserviert werden.

Den kenne ich, wenn ich das Linker File sehe aber das kenne ich eben 
nicht. Ich arbeite oft damit auf dem ARM, tut es auch alles. Und const 
... befördert Strings eben auch ins Flash. Was ich nicht weiss ob der 
Initilizer der startup sie wieder ins Ram holt.

von Christian J. (Gast)


Lesenswert?

Frank M. schrieb:
> Warum Du dafür aber eine eigene Funktion schreibst, ist mir
> schleierhaft. Letztendlich macht diese Funktion nichts anderes als
> strcpy_P().

Ist aus dem Arduino Forum geklaut...... wenn es im Internet steht muss 
es ja richtig sein :-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Ist aus dem Arduino Forum geklaut......

Die Code-Qualität bestätigt das übliche Vorurteil, das den 
Arduino-Programmiern anhaftet. Und dann wird dieser Mist auch noch 
ungesehen kopiert.

Wenn schon klauen, dann auch verstehen. Da hat man wesentlich mehr von 
;-)

: Bearbeitet durch Moderator
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Den kenne ich, wenn ich das Linker File sehe aber das kenne ich eben
> nicht.

Du siehst nur, wie groß der Heap insgesamt ist. Nicht, ob unter den 
unzähligen möglichen Programmabläufen jemals der Heap überlaufen kann.
Das Linker-Script ist irgendwo in der Arduino-Installation zu finden... 
Das sieht gut aus: arduino-1.8.8/hardware/tools/avr/avr/lib/ldscripts

Christian J. schrieb:
> Ich arbeite oft damit auf dem ARM, tut es auch alles.
Bis es dann halt irgendwann nicht mehr tut...

Christian J. schrieb:
> . Was ich nicht weiss ob der
> Initilizer der startup sie wieder ins Ram holt.
Nein. Zeiger auf String-Literale zeigen auf ARM direkt ins Flash.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Niklas G. schrieb:
> ein. Zeiger auf String-Literale zeigen auf ARM direkt ins Flash.

Führt zwar ein wenig weg aber darum schreibe ich die Dinge gern selbst, 
damit ich auch weis was passiert. Bei diesem Arduino weiss ich das eben 
nicht! Beim DIY Z80 mit dem sdcc habe ich das gemacht, wo der Heap hin 
gehört, wo der Stack und wie sie sich nicht in die Quere kommen. Die 
ganze Startup selbst geschrieben, ein gewisser Leo das aufwendige Linker 
Script. Und da tut es das auch.

Aber ansonsten ist Arduino ein tolles System, gar keine Frage.
1
    ;///////////////////////////////////////////////////////////
2
    ; Anordung der Segmente fuer den Linker, ab hier nur relative
3
    ; Adressen
4
5
      .area  _CODE
6
      .area  _INITIALIZER
7
      .area  _HOME
8
      .area  _GSINIT
9
      .area  _GSFINAL
10
      .area  _DATA
11
      .area  _INITIALIZED
12
      .area  _BSEG
13
      .area  _BSS
14
      .area  _HEAP
15
16
; ---  Defintion der Heap Groesse
17
__sdcc_heap_start::
18
      .ds heap_size
19
20
      .area _HEAP_END
21
__sdcc_heap_end::
22
      .ds 1

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Christian J. schrieb:
> damit ich auch weis was passiert

Der Sinn von Arduino ist, dass man sich damit eben nicht 
beschäftigen muss. Es steht dir frei, das Teil direkt mit AVR-GCC 
und/oder Atmel Studio zu programmieren...

von Christian J. (Gast)


Lesenswert?

Frank M. schrieb:
> Die Code-Qualität bestätigt das übliche Vorurteil, das den
> Arduino-Programmiern anhaftet. Und dann wird dieser Mist auch noch
> ungesehen kopiert.

Frank.... ich (50, Jg. 1968) gehöre in meinem Team zu den besten 
Programmierern. Nicht für CPUs (!), aber für Prüfautomaten, solche 
großen Schränke für den Endtest bei automatischen fertigungslinien. C# 
benutzen wir dafür mit Visual Studio und das HP Bus API für Oszis, 
Relais Matrizzen, Voltmeter usw. Die Stationen laden sich übers 
Firmennetz die Software, je nachdem was grad geprüft wird. COBOTS, 
fahren bald auf kleinen Wagen von allein an die Arbeitsplätze, weichen 
den Leuten aus und werkeln dann, rüsten ihre Werkzeuge selbst. Industrie 
4.0 ist das Thema.

Trotzdem kommen auch die Quereinsteiger zu Ergebnissen, die grad von den 
Schulen kommen, mit Arduino angefangen haben und die Summe aller Leute 
(34 sind wir) im Team behebt auch die Fehler, so dass das Ganze 
funktioniert. Einzel-Spitzenleistungen sind weniger gefragt. Du bist 
"spitze", wenn Du andere zum Erfolg anleiten und motivieren kannst.

von Toll ein anderer machts (Gast)


Lesenswert?

Christian J. schrieb:
> Frank.... ich (50, Jg. 1968) gehöre in meinem Team zu den besten
> Programmierern.

Dir ist hoffentlich klar, dass das nichts über dich aussagt.
Vielleicht ist auch euer Team sehr schlecht.

von Christian J. (Gast)


Lesenswert?

Toll ein anderer machts schrieb:
> Dir ist hoffentlich klar, dass das nichts über dich aussagt.
> Vielleicht ist auch euer Team sehr schlecht.

Sicher, darum fliegst du auch sicher mit Airbus Flugzeugen in den Urlaub 
und die stürzen nicht ab, weil wir die technische  Ausstattung 
fehlerhaft gebaut haben..... aber vielleicht ist ja eine Tupolew besser.

von Ich wer sonst (Gast)


Lesenswert?

Christian J. schrieb:
> Apropos ARM, mit dem 407 habe ich vor 2 Jahren mal was gemacht... plus 3
> weitere Displayanzeigen im Haus... , alles DIY :-) Nur dem EEPROM, da
> hat nie hingenauen mit der eingebauten Technik. Jetzt Software Ic2. Hab
> da Tage mit verbracht :-(

Mit welcher Lib oder GUI-Generator hast du den das Bund-Display zum 
laufen gebracht?

von Toll ein anderer machts (Gast)


Lesenswert?

Christian J. schrieb:
> Sicher, darum fliegst du auch sicher mit Airbus Flugzeugen in den Urlaub
> und die stürzen nicht ab, weil wir die technische  Ausstattung
> fehlerhaft gebaut haben.

Ich glaube du überschätzt dich und deine Arbeit etwas.
Deine Antwort bestätigt das wieder einmal.

Beitrag #5668371 wurde von einem Moderator gelöscht.
von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Ich wer sonst schrieb:
> Mit welcher Lib oder GUI-Generator hast du den das Bund-Display zum
> laufen gebracht?

ILI9341 SPi Version für STM32F103, beim F407 sind die Prescaler anders 
zu setzen und der APB ist auch ein anderer für die SPI.

von Christian J. (Gast)


Lesenswert?

Moin,

die obigen Sachen haben insgesa,t fast 400 Bytes an Eimsparungen 
gebract, also fast 1/3 weniger Ram als vorher!

Mit Strings tue ich mich noch etwas schwer, auch mangels Beispielen. 
Meine Lösung muss ja nicht die sein, die "optimal" ist.

Vorr: Der User - also ich erstmal - macht keine Eingabefehler! Die alle 
abzufangen dürfte erstmal jedes Maß sprengen. Kurz muss es sein.

Zeitfenster: 8.45 bis 17.30 Uhr
Eingabe: window<spc>08,45,17,30 oder
         window<spc>08.45,17.30

(<spc> = Leerzeichen)

ginge auch, da die Trenner nicht geprüft werden, Führende Null ist auch 
Pflicht, sonst murks....

Gibt es auch elegantere Lösungen? Das unten sind immerhin rund 240 Bytes 
Code ein paar Zahlen aus einem String zu ernten...
1
/* Start, Ende Uhrzeit */
2
typedef struct {
3
  byte hour;
4
  byte min;
5
} zeit_t;
6
7
int ParseWindow(char* buf, zeit_t* Start, zeit_t* End)
8
{  /*  window_08,45,17,30 */
9
10
  char* p = buf;
11
  
12
  /* Check cmd */
13
  if (!(strstr_P(buf,PSTR("window"))))
14
    return 0;
15
16
  /* Suche erstes Leerzeichen */
17
  p = strstr(buf," ");
18
  p++;
19
  Start->hour = ((p[0]-'0') * 10) + (p[1]-'0');
20
  Start->min  = ((p[3]-'0') * 10) + (p[4]-'0');
21
  End->hour   = ((p[6]-'0') * 10) + (p[7]-'0');
22
  End->min    = ((p[9]-'0') * 10) + (p[10]-'0');
23
  
24
  return 1;
25
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Du könntest eine Funktion bauen, die eine bis mehrere Zahlen "schluckt" 
(bis ein Trennzeichen auftritt, das keine Zahl ist), und sowohl den 
numerischen Wert als auch die Anzahl gelesener Zeichen zurückgibt.

Die Funktion besteht aus einer Schleife, die bei Auftreten einer Ziffer 
den vorherigen Wert mit zehn multipliziert und die Ziffer addiert, und 
abbricht, sobald das aktuelle Zeichen keine Ziffer mehr ist.

Damit bist Du die führenden Nullen los (und würdest aber auch den Fall 
versehentlicher mehrfacher führender Nullen "überleben").

Diese Funktion rufst Du viermal nacheinander auf, begonnen mit Deinem 
Zeichen nach dem ersten Leerzeichen (Hinweis: strchr dürfte hier 
effektiver sein als strstr).

von HyperMario (Gast)


Lesenswert?

Christian J. schrieb:
> Gibt es auch elegantere Lösungen? Das unten sind immerhin rund 240 Bytes
> Code ein paar Zahlen aus einem String zu ernten...

Evtl. eine andere Menüführung? Freie Eingaben weglassen und nur mit 
Cursor und +/- arbeiten. Dann entfällt das parsen, Fehleingaben und 
Vertipper sind nicht möglich und die Werte die der Anwender sieht sind 
nur nur Kopien vorhandener Variablen.

von Dirk B. (dirkb2)


Lesenswert?

Christian J. schrieb:
> ich schreibe auch if (a==true)
> statt nur
> das a, damit es leslicher ist.

Das sind dann aber unterschiedliche Aussagen.

(a!=false) wäre der Ersatz

: Bearbeitet durch User
von ho ho ho (Gast)


Lesenswert?

Dirk B. schrieb:
>> ich schreibe auch if (a==true)
>> statt nur
>> das a, damit es leslicher ist.
>
> Das sind dann aber unterschiedliche Aussagen.
>
> (a!=false) wäre der Ersatz

Was wieder beweist, wie sch... "C" ist. Sorry, muss sein, auch zu x-mas

von A. S. (Gast)


Lesenswert?

Dirk B. schrieb:
> Christian J. schrieb:
>> ich schreibe auch if (a==true) statt nur
>> das a, damit es leslicher ist.
>
> Das sind dann aber unterschiedliche Aussagen.
>
> (a!=false) wäre der Ersatz

Einmal das.

Zum anderen haben schon die Alten Griechen erkannt, wie blöd == true 
ist. Man kann das nämlich endlos weiter treiben zur Sicherheit und 
Klarheit,

If ((a==true)==true) {

von IstJaNochWeihnachten (Gast)


Lesenswert?

Hallo,
das hier hat nix mit dem Thema zu tun, aber ich finde
den Link oben von A.K. (tote Pferd) super klasse!!!
und so real ;-)
Guten Rutsch Euch allen.

von Christian J. (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Diese Funktion rufst Du viermal nacheinander auf, begonnen mit Deinem
> Zeichen nach dem ersten Leerzeichen (Hinweis: strchr dürfte hier
> effektiver sein als strstr).

Naja, mit strtol klappt es auch, da fallen die führenden Nullen auch weg 
und wenn nichts bei raus kommt ist es 0. Die Tests sind 
vielversprechend, jetzt mal in Ruhe optimieren aus dem Test Bett heraus 
ganz am Anfang des Programms.

Und bei einer sms kann man natürlich keine Führung einbauen.... der 
ganze Spass wird eh langsam teuer, jeder versuch kostet 9 cent......

Bei der Portierung auf den Cortex wird es eh lustig mit den ganzen 
Arduino speziellen Befehlen.....


1
 const char* cmd = "window";
2
 char* p;
3
4
 /* Schlüsselwort suchen */
5
 debugln(F("Suche window"));
6
 if (!(p = strstr(satz,cmd)))
7
    return;
8
  p = p + strlen(cmd);
9
  
10
  char *ptr;
11
  unsigned long zahl;
12
  zahl = strtol(p,&ptr,10);
13
  debugln(zahl);
14
  p = ptr + 1;

von Christian J. (Gast)


Lesenswert?

Nochmal zum Thema "malloc":

Ich verstehe nicht wieso man sich damit Probleme rein holen soll. Ich 
habe einiges ausprobiert und erstmal festgestellt, dass der 328P AVR es 
erlaubt

200 "belegt" Einträge zu machen
und 860 Bytes Heap zur Verfügung stellt, von 2k.

Eine Fragmentierung kann es nicht geben, wenn man nur 1 Zeiger verwendet 
und diesen auch sofort wieder frei gibt.  Auch niccht bei 2,3 und mehr, 
solange diese konsequent frei gegeben werden, bevor die Routine 
verlassen wird.

Der Heap liegt ja unter dem Stack, d.h. der Stack kann rein wachsen, 
wenn man ihn zu gross werden lässt.

Es funktioniert jedenfalls.... mit kleinen Größen, also derzeit maxiaml 
80 Bytes.

Ich finde diese dynamische Sache nämlich eigentlich prima, früher oft 
benutzt bei binären Bäumen in Pascal und C.

Gruss,
Christian

von Rolf M. (rmagnus)


Lesenswert?

Christian J. schrieb:
> Nochmal zum Thema "malloc":
>
> Ich verstehe nicht wieso man sich damit Probleme rein holen soll. Ich
> habe einiges ausprobiert und erstmal festgestellt, dass der 328P AVR es
> erlaubt
>
> 200 "belegt" Einträge zu machen
> und 860 Bytes Heap zur Verfügung stellt, von 2k.
>
> Eine Fragmentierung kann es nicht geben, wenn man nur 1 Zeiger verwendet
> und diesen auch sofort wieder frei gibt.  Auch niccht bei 2,3 und mehr,
> solange diese konsequent frei gegeben werden, bevor die Routine
> verlassen wird.

Aber wozu dann malloc? Das braucht zusätzliche Rechenzeit, es produziert 
Overhead für seine interne Verwaltung, und man braucht letztendlich auch 
den zusätzlichen Pointer für den Zugriff. Weniger Speicher als wenn die 
gleichen Daten auf dem Stack liegen würden, braucht es nicht.

> Es stellt sich nur noch die Frage, wo der Heap sich denn so rum lümmelt?
> Vermutlich unter dem Stack, d.h. der Stack kann rein wachsen, wenn man
> ihn zu gross werden lässt.

Ja.

von Christian J. (Gast)


Lesenswert?

Rolf M. schrieb:
> Aber wozu dann malloc?

Weil es das gibt? ... ähm.... nee, natürlich nicht. Ich vermute eher das 
das nur Sinn macht wenn man echt komplexe Datenbäume hat und viel 
Speicher. Aber auf einem AVR mit 2kB..... naja.. der baum würde sehr 
schnell über den kleinen Blumentopf hinaus wachsen wollen.

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> Ich verstehe nicht wieso man sich damit Probleme rein holen soll.

Nun, man muß zur Compilezeit festlegen, wieviel RAM darf Malloc und 
wieviel der Stack belegen. D.h. es kracht, sobald einer von beiden 
ausgeht, obwohl noch RAM übrig sein kann.

Christian J. schrieb:
> Auch niccht bei 2,3 und mehr,
> solange diese konsequent frei gegeben werden, bevor die Routine
> verlassen wird.

Dann nimmt man aber kein Malloc, sondern einfach lokale Variablen.
Malloc braucht man nur, wenn verschiedene Tasks die Variablen anlegen 
und freigeben sollen. Z.B. der Ethernetinterrupt speichert ein 
Datenpaket und die Mainloop parst es und gibt es wieder frei.

von avr (Gast)


Lesenswert?

Hier ein Vorschlag, bei dem alles über den Command-String gesteuert 
wird. Sehr flexibel ist das nicht, aber müsste recht klein sein. In 
Assembler sollten es um die 60 Byte sein, bin gespannt was der Compiler 
draus macht.
1
// Aufruf
2
Parse("window 18.45,19.53","window \xC0\x80.\xC1\x81,\xC2\x82.\xC3\x83", buf);
3
4
int Parse(char* input, const char* command, uint8_t* buf)
5
{
6
  uint8_t cmd_ch, in_ch;
7
  do
8
  {
9
    in_ch = *input++;
10
    cmd_ch = *command++;
11
 
12
    if(cmd_ch & 0x80)
13
    {
14
      in_ch -= '0';
15
      if(cmd_ch & 0x40)
16
        in_ch *= 10;
17
      buf[cmd_ch & 0x3F] += in_ch;
18
    }
19
    else if(in_ch != cmd_ch)
20
      return 0;
21
  }
22
  while(in_ch * cmd_ch);
23
  return 1;
24
}

von Christian J. (Gast)


Lesenswert?

Naja, das sieht schon elegant aus. Meine Lösung ist eher so wie man sie 
sich zuerst überlegt, ohne mit Bits zu basteln. Aber ich nehme den 
gesamten Header mit, den so eine sms hat. Hat den Vorteil, dass die 
führende Null egal ist. strtol verbraucht aber 1,2kb extra. p = ptr + 1; 
schiebt den Zeiger über das Komma weg.

Was ist das?

while(in_ch * cmd_ch);
1
int ParseWindow(char* buf, zeit_t* Start, zeit_t* End)
2
{  
3
  /*  window_08,45,17,30 */
4
      
5
  /* Schlüsselwort suchen */
6
  char* p;
7
  if (!(p = strstr_P(buf,PSTR("window"))))
8
    return 0;
9
  p = p + strlen_P(PSTR("window"));
10
  
11
  /* Zahlen extrahieren */
12
  char *ptr;
13
  byte res[4];
14
  for (int i = 0; i < 4; i++) {
15
    res[i] = strtol(p,&ptr,10);
16
    p = ptr + 1;
17
  }
18
19
  /* Bereichsprüfungen */
20
  if ((res[0] > 23) || (res[2] > 23))
21
    return 0;
22
  if ((res[1] > 59) || (res[3] > 59))
23
    return 0;
24
    
25
  /* Zuweisung */
26
  Start->hour = res[0];
27
  Start->min  = res[1];
28
  End->hour   = res[2];
29
  End->min    = res[3];
30
  
31
  return 1;
32
}

von avr (Gast)


Lesenswert?

Christian J. schrieb:
> while(in_ch * cmd_ch);

Das ergibt genau dann 0 (also false), wenn mindestens eine der Variablen 
0 ist.

Im Endeffekt kodiert man im Command-String mit Platzhaltern, wo die 
Zahlen hingeschrieben werden sollen. Mit 0x8n wird die Zahl in das n-te 
Byte von buf addiert, mit 0xCn wird sie zusätzlich mit 10 multipliziert. 
buf muss daher auch zwingend vorher 0 sein.

von Rolf M. (rmagnus)


Lesenswert?

avr schrieb:
> Christian J. schrieb:
>> while(in_ch * cmd_ch);
>
> Das ergibt genau dann 0 (also false), wenn mindestens eine der Variablen
> 0 ist.

Dann würde ich aber schreiben:
1
while (in_ch && cmd_ch);

oder von mir aus auch ausführlich:
1
while (in_ch != 0 && cmd_ch != 0);

Aber nicht so verklausuliert über eine Multiplikation.

von avr (Gast)


Lesenswert?

Rolf M. schrieb:
> Aber nicht so verklausuliert über eine Multiplikation.

Die Frage ist, ob der Compiler daraus Multiplikation erzeugt und damit 
die besseren Maschinencode für den avr. Ich vermute nicht. Da der Code 
klein werden sollte, habe ich die Optimierung direkt eingesetzt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.