Mehr Ram für einen ATMega328

Guten Tag zusammen,

ich habe einen Code der zwar nicht komplex oder groß ist, aber viel Speicherplatz für Variablen belegt.

Konkret:
Programmspeicher: 24.638/30.720Bytes (80%)
Dynamischer Speicher: 1.638/2.048Bytes (79%)

Die IDE sagt mir auch schon, dass es Stabilitätsprobleme geben könnte.
Wenn der Sketch nur kurz läuft, funktioniert es auch ohne Probleme, nur wenn er länger läuft und mehrere Requests bearbeiten musste (Nicht gleichzeitig sondern ca. 30 Sec. auseinander) schmiert er einfach ab.
Mit “abschmieren” meine ich: Er reagiert nicht mehr auf eingehende Verbindungen und über die Serielle Schnittstelle gibt er nur noch diese “Fragenzeichenblöcke” aus. Also einen ASCII Wert der nicht zwischen 33 und 126 liegt. Schätze mal an dieser Stelle gibt er nur irgendwelchen RAM garbage aus.

Meine Vermutung ist, dass es wirklich an dem wenig freien Speicherplatz liegt. Wenn ich z.B. das komplette struct “ircodes” herausnehme sind wieder 807 Bytes für den RAM frei und alles läuft stabil.

Jetzt ist die Frage: Wie kann ich den Sketch kleiner gestalten, oder gibt es einen ähnlichen Chip wie den 328 nur mit mehr Ram? Einen ATmega2560 möchte ich ungern nehmen, da die Pins einfach nicht benötigt werden, das Projekt auch noch relativ klein gehalten werden soll und ich nicht das Werkzeug und Kenntnisse für SMD Bauteile habe.

Hier noch der Code:

#define WLAN_SSID       "AP"
#define WLAN_PASS       "Passwort"
#define WLAN_SECURITY   WLAN_SEC_WPA2
#define LISTEN_PORT           7
#define ADAFRUIT_CC3000_IRQ   2
#define ADAFRUIT_CC3000_VBAT  5
#define ADAFRUIT_CC3000_CS    10
#define IR_RATE 92
#define PWM_PIN 6

#include <Adafruit_CC3000.h>
#include <SPI.h>
#include <IRremote.h>
#include "utility/debug.h"
#include "utility/socket.h"


Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ,
                         ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIVIDER);


int validatetele();

Adafruit_CC3000_Server echoServer(LISTEN_PORT);
IRsend irsend;

struct ircodes_struc {
  char name[10];
  long code[5];
};

const ircodes_struc ircodes[13] = {{"rot  ", {0xD720DF, 0xF710EF, 0xF730CF, 0xF708F7, 0xF728D7}},
  {"gruen", {0xF7A05F, 0xF7906F, 0xF7B04F, 0xF78877, 0xF7A857}},
  {"blau ", {0xF7609F, 0xF750AF, 0xF7708F, 0xF748B7, 0xF76897}},
  {"weiss", {0xF7E01F}},
  {"on", {0xF7C03F}},
  {"off", {0xF740BF}},
  {"dimm_up", {0xF700FF}},
  {"dimm_down", {0xF7807F}},
  {"flash", {0xF7D02F}},
  {"strobe", {0xF7F00F}},
  {"fade", {0xF7C837}},
  {"smooth", {0xF7E817}}
};

typedef struct
{
  char header[2];
  char tart[5];
  char message_type[1];
  char tele_id[6];
  char auftrag_id[6];
  char nutzdaten[20];
} SOC_telegramm;

typedef struct
{
  char header[2];
  char teleid[6];
  char tart[5];
  char returncode[2];
} SOC_quittung;

SOC_telegramm recv_buff;
SOC_quittung quittung;

int verbunden = 0;
int old_val = 0;
char ip[15];

void send_ir(long code)
{
  Serial.println(F("Sende"));
  for (int x = 0; x != 3; x++)
  {
    irsend.sendNEC(code, IR_RATE);
    delay(40);
  }
}

void send_na()
{
  uint32_t ipadress = cc3000.IP2U32(192, 168, 178, 24);
  Adafruit_CC3000_Client www = cc3000.connectTCP(ipadress, 8888);
  if (www.connected()) {
    Serial.println("SENDE NA");
    strncpy(recv_buff.header, "HE", 2);
    strncpy(recv_buff.tart, "NA   ", 5);
    strncpy(recv_buff.message_type, "N", 1);
    strncpy(recv_buff.tele_id, "000001", 6);
    strncpy(recv_buff.auftrag_id, "      ", 6);
    strncpy(recv_buff.nutzdaten, "LEDLI", 5);

    www.write((char*)&recv_buff, 25);
  }
  else
  {
    Serial.println(F("Connection failed"));
    return;
  }

  while (www.available()) {
    char c = www.read();
    Serial.print(c);
  }
  www.close();
  memset((char*)&recv_buff, '\0', sizeof(recv_buff));
}

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

  strncpy(quittung.header, "QU", 2);
  Serial.println(F("Start"));

  if (!cc3000.begin())
  {
    Serial.println(F("Couldn't begin()! Check your wiring?"));
    while (1);
  }

  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY))
  {
    Serial.println(F("Failed!"));
    while (1);
  }

  while (!cc3000.checkDHCP())
  {
    delay(100);
  }

  pinMode(PWM_PIN, OUTPUT);

  while (! displayConnectionDetails()) {
    delay(1000);
  }
  Serial.println(F("Los gehts"));

  echoServer.begin();
  send_na();
  Serial.println(F("Starte loop"));

}

void loop(void)
{
  Adafruit_CC3000_ClientRef client = echoServer.available();
  if (client)
  {
    Serial.println("neuer Client");
    if (client.available() > 0)
    {
      uint8_t ch = client.read((char*)&recv_buff, sizeof(recv_buff));
      memcpy(quittung.teleid, recv_buff.tele_id, 6);
      memcpy(quittung.tart, recv_buff.tart, 5);
      if (validatetele() < 0)
        memcpy(quittung.returncode, "01", 2);
      else
        memcpy(quittung.returncode, "00", 2);
      client.write((char*)&quittung, sizeof(quittung));
      verbunden = 1;
    }
    if (verbunden)
    {
      if (strncmp("LED  ", recv_buff.tart, 5) == 0)
      {
        /*Serial.println("LED");
        Serial.println("Set Weissen LED Stripe zu");
        Serial.println(recv_buff.nutzdaten);*/
        int val = atoi(recv_buff.nutzdaten);
        if (val != old_val)
        {
          if (val > old_val)
          {
            while (val != old_val)
            {
              old_val++;
              analogWrite(PWM_PIN, old_val);
              delay(10);
            }
            //Serial.println(F("Fertig"));
          }
          else
          {
            while (val < old_val)
            {
              old_val--;
              analogWrite(PWM_PIN, old_val);
              delay(10);
            }
            //Serial.println(F("Fertig"));
          }
          uint32_t ipadress = cc3000.IP2U32(192, 168, 178, 24);
          Adafruit_CC3000_Client www = cc3000.connectTCP(ipadress, 8888);
          if (www.connected()) 
          {
            Serial.println(F("SENDE LED"));
            strncpy(recv_buff.header, "HE", 2);
            strncpy(recv_buff.tart, "LEDST", 5);
            strncpy(recv_buff.message_type, "N", 1);
            strncpy(recv_buff.tele_id, "000001", 6);
            strncpy(recv_buff.auftrag_id, "      ", 6);
            sprintf(recv_buff.nutzdaten, "%d", old_val);
            //strncpy(recv_buff.nutzdaten, (char*), 5);

            www.write((char*)&recv_buff, 23);
          }
          else
          {
            Serial.println(F("Connection failed"));
            return;
          }

          while (www.available()) 
          {
            char c = www.read();
            Serial.print(c);
          }
          www.close();
          memset((char*)&recv_buff, '\0', sizeof(recv_buff));
        }
        Serial.println(F("zurueck in IDLE"));
      }
      else if (strncmp("RGB  ", recv_buff.tart, 5) == 0)
      {
        for (int i = 0; i != 12; i++)
        {
          if (strcmp(ircodes[i].name, recv_buff.nutzdaten) == 0)
          {
            send_ir(ircodes[i].code[0]);
          }
          else if (strncmp(ircodes[i].name, recv_buff.nutzdaten, 5) == 0)
          {
            int code_index = atoi(&recv_buff.nutzdaten[5]);
            send_ir(ircodes[i].code[code_index]);
          }
        }
      }
      memset(recv_buff.nutzdaten, '\0', sizeof(recv_buff.nutzdaten));
      verbunden = 0;
      Serial.println(F("zurueck in IDLE"));
    }
  }
}

bool displayConnectionDetails(void)
{
  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;

  if (!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
  {
    Serial.println(F("Unable to retrieve the IP Address!\r\n"));
    return false;
  }
  else
  {
    Serial.println((uint8_t)(ipAddress)); */
    Serial.println(test1);
    Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
    Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
    Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
    Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
    Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
    Serial.println();
    return true;
  }
}

int validatetele()
{
  //TODO: Realisieren
  return 0;
}

audacity363: Meine Vermutung ist, dass es wirklich an dem wenig freien Speicherplatz liegt. Wenn ich z.B. das komplette struct "ircodes" herausnehme sind wieder 807 Bytes für den RAM frei und alles läuft stabil.

Du könntest die Struktur in den Programmspeicher verlagern, sie scheint mir nur gelesen zu werden, oder?

Jap wird sie. Danke daran habe ich gar nicht gedacht. Laut IDE ist jetzt wieder alles in Ordnung. Werde es heute Abend einmal ausprobieren.

Whandall: Du könntest die Struktur in den Programmspeicher verlagern, sie scheint mir nur gelesen zu werden, oder?

Noch nie gehört - wie macht man das?

Klaus_ww: Noch nie gehört - wie macht man das?

Mal ein Beispiel mit meinem Code oben:

const ircodes_struc ircodes[13] PROGMEM = {{"rot  ", {0xD720DF, 0xF710EF, 0xF730CF, 0xF708F7, 0xF728D7}},
  {"gruen", {0xF7A05F, 0xF7906F, 0xF7B04F, 0xF78877, 0xF7A857}},
  {"blau ", {0xF7609F, 0xF750AF, 0xF7708F, 0xF748B7, 0xF76897}},
  {"weiss", {0xF7E01F}},
  {"on", {0xF7C03F}},
  {"off", {0xF740BF}},
  {"dimm_up", {0xF700FF}},
  {"dimm_down", {0xF7807F}},
  {"flash", {0xF7D02F}},
  {"strobe", {0xF7F00F}},
  {"fade", {0xF7C837}},
  {"smooth", {0xF7E817}}
};

Dabei bewirkt das "PROGMEM", dass die Variable in den Programmspeicher gelegt wird. Allerdings ist diese dann Read Only (Was ja auch das "const" schon sagt).

audacity363: Mal ein Beispiel mit meinem Code oben:

Ich lese gerade mit, was meinst du mit "Code von oben" ?

Schätze das soll structs darstellen, kannst du das bitte noch ein wenig ausführen (also die zugehörigen Structs zeigen) ?

Viele Grüße

Harry

HarryR:
Ich lese gerade mit, was meinst du mit “Code von oben” ?

Mein Code aus dem ersten Post (Hier jetzt schon mit PROGMEM):

#define WLAN_SSID       "AP"
#define WLAN_PASS       "Passwort"
#define WLAN_SECURITY   WLAN_SEC_WPA2
#define LISTEN_PORT           7
#define ADAFRUIT_CC3000_IRQ   2
#define ADAFRUIT_CC3000_VBAT  5
#define ADAFRUIT_CC3000_CS    10
#define IR_RATE 92
#define PWM_PIN 6

#include <Adafruit_CC3000.h>
#include <SPI.h>
#include <IRremote.h>
#include "utility/debug.h"
#include "utility/socket.h"


Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ,
                         ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIVIDER);


int validatetele();

Adafruit_CC3000_Server echoServer(LISTEN_PORT);
IRsend irsend;

struct ircodes_struc {
  char name[10];
  long code[5];
};

const ircodes_struc ircodes[13] PROGMEM = {{"rot  ", {0xD720DF, 0xF710EF, 0xF730CF, 0xF708F7, 0xF728D7}},
  {"gruen", {0xF7A05F, 0xF7906F, 0xF7B04F, 0xF78877, 0xF7A857}},
  {"blau ", {0xF7609F, 0xF750AF, 0xF7708F, 0xF748B7, 0xF76897}},
  {"weiss", {0xF7E01F}},
  {"on", {0xF7C03F}},
  {"off", {0xF740BF}},
  {"dimm_up", {0xF700FF}},
  {"dimm_down", {0xF7807F}},
  {"flash", {0xF7D02F}},
  {"strobe", {0xF7F00F}},
  {"fade", {0xF7C837}},
  {"smooth", {0xF7E817}}
};

typedef struct
{
  char header[2];
  char tart[5];
  char message_type[1];
  char tele_id[6];
  char auftrag_id[6];
  char nutzdaten[20];
} SOC_telegramm;

typedef struct
{
  char header[2];
  char teleid[6];
  char tart[5];
  char returncode[2];
} SOC_quittung;

SOC_telegramm recv_buff;
SOC_quittung quittung;

int verbunden = 0;
int old_val = 0;
char ip[15];

void send_ir(long code)
{
  Serial.println(F("Sende"));
  for (int x = 0; x != 3; x++)
  {
    irsend.sendNEC(code, IR_RATE);
    delay(40);
  }
}

void send_na()
{
  uint32_t ipadress = cc3000.IP2U32(192, 168, 178, 24);
  Adafruit_CC3000_Client www = cc3000.connectTCP(ipadress, 8888);
  if (www.connected()) {
    Serial.println("SENDE NA");
    strncpy(recv_buff.header, "HE", 2);
    strncpy(recv_buff.tart, "NA   ", 5);
    strncpy(recv_buff.message_type, "N", 1);
    strncpy(recv_buff.tele_id, "000001", 6);
    strncpy(recv_buff.auftrag_id, "      ", 6);
    strncpy(recv_buff.nutzdaten, "LEDLI", 5);

    www.write((char*)&recv_buff, 25);
  }
  else
  {
    Serial.println(F("Connection failed"));
    return;
  }

  while (www.available()) {
    char c = www.read();
    Serial.print(c);
  }
  www.close();
  memset((char*)&recv_buff, '\0', sizeof(recv_buff));
}

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

  strncpy(quittung.header, "QU", 2);
  Serial.println(F("Start"));

  if (!cc3000.begin())
  {
    Serial.println(F("Couldn't begin()! Check your wiring?"));
    while (1);
  }

  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY))
  {
    Serial.println(F("Failed!"));
    while (1);
  }

  while (!cc3000.checkDHCP())
  {
    delay(100);
  }

  pinMode(PWM_PIN, OUTPUT);

  while (! displayConnectionDetails()) {
    delay(1000);
  }
  Serial.println(F("Los gehts"));

  echoServer.begin();
  send_na();
  Serial.println(F("Starte loop"));

}

void loop(void)
{
  Adafruit_CC3000_ClientRef client = echoServer.available();
  if (client)
  {
    Serial.println("neuer Client");
    if (client.available() > 0)
    {
      uint8_t ch = client.read((char*)&recv_buff, sizeof(recv_buff));
      memcpy(quittung.teleid, recv_buff.tele_id, 6);
      memcpy(quittung.tart, recv_buff.tart, 5);
      if (validatetele() < 0)
        memcpy(quittung.returncode, "01", 2);
      else
        memcpy(quittung.returncode, "00", 2);
      client.write((char*)&quittung, sizeof(quittung));
      verbunden = 1;
    }
    if (verbunden)
    {
      if (strncmp("LED  ", recv_buff.tart, 5) == 0)
      {
        /*Serial.println("LED");
        Serial.println("Set Weissen LED Stripe zu");
        Serial.println(recv_buff.nutzdaten);*/
        int val = atoi(recv_buff.nutzdaten);
        if (val != old_val)
        {
          if (val > old_val)
          {
            while (val != old_val)
            {
              old_val++;
              analogWrite(PWM_PIN, old_val);
              delay(10);
            }
            //Serial.println(F("Fertig"));
          }
          else
          {
            while (val < old_val)
            {
              old_val--;
              analogWrite(PWM_PIN, old_val);
              delay(10);
            }
            //Serial.println(F("Fertig"));
          }
          uint32_t ipadress = cc3000.IP2U32(192, 168, 178, 24);
          Adafruit_CC3000_Client www = cc3000.connectTCP(ipadress, 8888);
          if (www.connected()) 
          {
            Serial.println(F("SENDE LED"));
            strncpy(recv_buff.header, "HE", 2);
            strncpy(recv_buff.tart, "LEDST", 5);
            strncpy(recv_buff.message_type, "N", 1);
            strncpy(recv_buff.tele_id, "000001", 6);
            strncpy(recv_buff.auftrag_id, "      ", 6);
            sprintf(recv_buff.nutzdaten, "%d", old_val);
            //strncpy(recv_buff.nutzdaten, (char*), 5);

            www.write((char*)&recv_buff, 23);
          }
          else
          {
            Serial.println(F("Connection failed"));
            return;
          }

          while (www.available()) 
          {
            char c = www.read();
            Serial.print(c);
          }
          www.close();
          memset((char*)&recv_buff, '\0', sizeof(recv_buff));
        }
        Serial.println(F("zurueck in IDLE"));
      }
      else if (strncmp("RGB  ", recv_buff.tart, 5) == 0)
      {
        for (int i = 0; i != 12; i++)
        {
          if (strcmp(ircodes[i].name, recv_buff.nutzdaten) == 0)
          {
            send_ir(ircodes[i].code[0]);
          }
          else if (strncmp(ircodes[i].name, recv_buff.nutzdaten, 5) == 0)
          {
            int code_index = atoi(&recv_buff.nutzdaten[5]);
            send_ir(ircodes[i].code[code_index]);
          }
        }
      }
      memset(recv_buff.nutzdaten, '\0', sizeof(recv_buff.nutzdaten));
      verbunden = 0;
      Serial.println(F("zurueck in IDLE"));
    }
  }
}

bool displayConnectionDetails(void)
{
  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;

  if (!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
  {
    Serial.println(F("Unable to retrieve the IP Address!\r\n"));
    return false;
  }
  else
  {
    Serial.println((uint8_t)(ipAddress)); */
    Serial.println(test1);
    Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
    Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
    Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
    Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
    Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
    Serial.println();
    return true;
  }
}

int validatetele()
{
  //TODO: Realisieren
  return 0;
}

Den Zugriff auf die Struktur musst du noch ändern, das wird sonst nicht funktionieren.

audacity363: struct ircodes_struc { char name[10]; long code[5]; };

const ircodes_struc ircodes[13] PROGMEM = {{"rot ", {0xD720DF, 0xF710EF, 0xF730CF, 0xF708F7, 0xF728D7}}, {"gruen", {0xF7A05F, 0xF7906F, 0xF7B04F, 0xF78877, 0xF7A857}}, {"blau ", {0xF7609F, 0xF750AF, 0xF7708F, 0xF748B7, 0xF76897}}, {"weiss", {0xF7E01F}}, {"on", {0xF7C03F}}, {"off", {0xF740BF}}, {"dimm_up", {0xF700FF}}, {"dimm_down", {0xF7807F}}, {"flash", {0xF7D02F}}, {"strobe", {0xF7F00F}}, {"fade", {0xF7C837}}, {"smooth", {0xF7E817}} };

Hmm,

ab ircodes[3] wirst du aber Schwierigkeiten bekommen, da das abgespeicherte dann nicht mehr dem struct entspricht. Ich würde, wenn der Speicher reicht, mit Dummy-Werten ergänzen um auf die 5 long(s) zu kommen.

Grüße

Harry

Es gibt memcpy_P() um Daten aus dem Flash ins RAM zu kopieren. Funktioniert wie memcpy() nur dass man als zweiten Parameter keine Adresse im RAM übergibt, sondern eine Adresse der Daten im Flash.

Da kann man dann gezielt eine der Zeilen des Arrays ins RAM kopieren. Für die Länge sollte sizeof() gehen. Habe das aber so noch nie ausprobiert.

Details kann man hier finden http://www.atmel.com/webdoc/AVRLibcReferenceManual/pgmspace.html.

Hmm,

ab ircodes[3] wirst du aber Schwierigkeiten bekommen, da das abgespeicherte dann nicht mehr dem struct entspricht. Ich würde, wenn der Speicher reicht, mit Dummy-Werten ergänzen um auf die 5 long(s) zu kommen.

Hmm Harry, du kannst dir vermutlich nicht vorstellen, wie schlau der avr-gcc Compiler ist, wenn er nur will. Da er weiss, dass es 5 longs sind, wird der Platz dafür auch freigehalten, ähnlich wie der Platz für 9+1 Buchstaben freigehalten wird, selbst wenn er nur mit "rot" ( 4 byte ) initialisiert wird.

Ob ircodes[3].code[1] = 0 sinnvoll ist ( selbst das ist für globale Variable und array-strucs sichergestellt ) muss jemand anders wissen.

michael_x: Hmm Harry, du kannst dir vermutlich nicht vorstellen, wie schlau der avr-gcc Compiler ist, wenn er nur will. Da er weiss, dass es 5 longs sind, wird der Platz dafür auch freigehalten, ähnlich wie der Platz für 9+1 Buchstaben freigehalten wird, selbst wenn er nur mit "rot" ( 4 byte ) initialisiert wird.

Ob ircodes[3].code[1] = 0 sinnvoll ist ( selbst das ist für globale Variable und array-strucs sichergestellt ) muss jemand anders wissen.

Jetzt kann ich es schon ;D Ich hätte eher vermutet, dass er aus Sparsamkeitsgründen nicht definierte longs wegoptimiert. Dann ist es auch egal, wie man die nicht benötigten longs initialisiert, der Programmierer muss nur dafür sorgen, dass er sich die Werte holt die benötigt werden.

Wo auch noch Optimierungspotential besteht ist bei printf(). Weglassen und statt dessen itoa() verwenden! Braucht sowohl weniger Flash als auch temporär(!) weniger RAM.