Include auf SD Card auslagern

Hallo

Ich arbeite mit dem Arduino Ethernet.
Mein Programm ist mittlerweile so angewachsen, dass der interne Flashspeicher zu klein dafür ist.
Meines Erachtens sind vorallem die Includes schuld:

#include <OneWire.h>
#include <Ethernet.h>
#include <SPI.h>
#include <TextFinder.h>
#include <SD.h>

Der Arduino Ethernet besitzt einen SD Card Slot. Ist es möglich diesen unter anderem als “Erweiterungsspeicher” zu nutzen?

lg

Nein.

Zeig lieber den kompletten Sketch. Die Probleme deinerseits können wo ganz anders sitzen. Zum Beispiel wenn du Textausgaben ohne PROGMEM machst.

Der Atmega328P ist nur bedingt für Ethnert aufgrund des geringen Speichers möglich. Sonderlich große Programme sind da so nicht möglich.

Das ist mein Sketch. Er dient zur Überwachung einer Heizungsanlage.

Teil 1

#include <OneWire.h>
#include <Ethernet.h>
#include <SPI.h>
#include <TextFinder.h>
#include <SD.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0xC8, 0x0F }; // Arduino Ethernet
byte ip[] = { 192,168,1,178};
String strZielOel = "/Heizung/input_oelzaehler.php";
String strZielTemperatur = "/Heizung/input_temperatur.php";

byte gateway[] = { 192, 168, 1, 2 };
byte subnet[] = { 255, 255, 0, 0 };

EthernetClient client2;

byte sdPin = 4;               // Pin der SD-Karte
EthernetServer server(80);    // Server port
File webFile;

boolean ImpulsAktiv = false;
const int OilCounterPin = 3;

const int PinBus1 = 2;
const int PinBus2 = 5;
int IntervalBus1 = 0;
int IntervalBus2 = 0;

// OneWire DS18S20, DS18B20, DS1822 Temperature Example
// http://www.pjrc.com/teensy/td_libs_OneWire.html
// The DallasTemperature library can do all this work for you!
// http://milesburton.com/Dallas_Temperature_Control_Library

OneWire  Bus1(PinBus1);  //(a 4.7K resistor is necessary)
OneWire  Bus2(PinBus2);

void setup(void) {
  Serial.begin(9600);
  Ethernet.begin(mac, ip, gateway, subnet);  
  server.begin();          // Server starten
  pinMode(OilCounterPin, INPUT);
  
   Serial.println("Initialisiere SD-Karte...");
  if (!SD.begin(sdPin)) 
  {
    Serial.println(" - Initialisierung der SD-Karte fehlgeschlagen!");
    return;
  }
  Serial.println(" - SD-Karte erfolgreich initialisiert.");

  if (!SD.exists("index.htm")) 
  {
    Serial.println(" - Datei (index.htm) wurde nicht gefunden!");
    return;
  }
  Serial.println(" - Datei (index.htm) wurde gefunden.");  
}

void loop(void) {
  EthernetClient client = server.available(); // Auf Anfrage warten

  if(client)
  {
    /*****************************************
      Ausgänge über das Webformular steuern  *
    *****************************************/
    TextFinder finder(client);

    if(finder.find("GET"))
    {
      while(finder.findUntil("pin", "\n\r"))
      {
        char typ = client.read();
        int  pin = finder.getValue();
        int  val = finder.getValue();

        if(typ == 'D')
        {
          pinMode(pin, OUTPUT);
          digitalWrite(pin, val);
          Serial.print(" - D"+String(pin));
        }
        else if(typ == 'A')
        {
          analogWrite(pin, val);
          Serial.print(" - A"+String(pin));
        }
        else Serial.print(" - Falscher Typ");

        if(val==1) Serial.println(" ein"); else Serial.println(" aus");
      }
    }

    /************************
      Webformular anzeigen  *
    ************************/
    boolean current_line_is_blank = true;       // eine HTTP-Anfrage endet mit einer Leerzeile und einer neuen Zeile

    while (client.connected()) 
    {
      if (client.available())                   // Wenn Daten vom Server empfangen werden
      {
        char c = client.read();                 // empfangene Zeichen einlesen
        if (c == '\n' && current_line_is_blank) // wenn neue Zeile und Leerzeile empfangen
        { // Standard HTTP Header senden
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println();

          // Website von SD-Karte laden
          webFile = SD.open("index.htm");  // Website laden
          if (webFile)
          {
            while(webFile.available())
            {
              client.write(webFile.read()); // Website an Client schicken
            }
            webFile.close();
          }
          break;
        }
        if (c == '\n') 
        {
          current_line_is_blank = true;
        } 
        else if (c != '\r') 
        {
          current_line_is_blank = false;
        }
      }
    }
    delay(1);
    client.stop();
  }
  
  
  
  
  //Ölzähler
  OilCounter();


  //Bus 1
  IntervalBus1 += 1;
  if (IntervalBus1 == 5) {
    Serial.println("Suche Sensoren in Bus1:");
    SucheInBus1();
    IntervalBus1 = 0;
  }

  //Bus 2 
  IntervalBus2 += 1;
  if (IntervalBus2 == 60) {
    Serial.println("Suche Sensoren in Bus2:");
    SucheInBus2();
    IntervalBus2 = 0;
  }


  //delay(1000); //Zeit bis zum nächsten Loop
}

void OilCounter() {
  //Ölzähler
  Serial.println("Oelzaehler pruefen");
  Serial.println();
  if (digitalRead(OilCounterPin) == LOW) {
    if (ImpulsAktiv == false) {
      post("wert1=ArduinoHeizung_Oil_1DL&Send=Eintragen", strZielOel);
      ImpulsAktiv = true;
    }
  }
  else {
    ImpulsAktiv = false;
  }
}

void SucheInBus1() {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius;
  String strROM;
  String strChip;
  String strData;
  String strCRC;


  if ( !Bus1.search(addr)) {
    Serial.println("Bus1: No more addresses.");
    Serial.println();
    Bus1.reset_search();
    return;
  }

  Serial.print("ROM =");
  for ( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
    strROM += String(addr[i], HEX);
  }
  if (OneWire::crc8(addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return;
  }
  Serial.println();

  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      strChip = "DS18S20";
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      strChip = "DS18B20";
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      strChip = "DS1822";
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      strChip = "Device is not a DS18x20 family device";
      return;
  }

  Bus1.reset();
  Bus1.select(addr);
  Bus1.write(0x44, 1);        // start conversion, with parasite power on at the end

  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.

  present = Bus1.reset();
  Bus1.select(addr);
  Bus1.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  strData = String(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = Bus1.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
    strData +=  "-" + String(data[i], HEX);
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  strCRC = String(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  }
  else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;

  Serial.print("  Temperature = ");
  Serial.print(celsius);
  char buffer[12];
  String tempStr = dtostrf(celsius, 6, 2, buffer);
  tempStr.trim();
  Serial.print(" Celsius");
  Serial.println();
  Serial.println();
  //In DB speichern
  post("strROM=" + strROM + "&strChip=" + strChip + "&strData=" + strData + "&strCRC=" + strCRC + "&strTemperatur=" + tempStr + "&Send=Eintragen", strZielTemperatur);
  Serial.println("Bus1: Temperaturen in Datenbank speichern: " + strROM + " = " + tempStr);
  Serial.println();
}

Teil 2:

void SucheInBus2() {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius;
  String strROM;
  String strChip;
  String strData;
  String strCRC;


  if ( !Bus2.search(addr)) {
    Serial.println("Bus2: No more addresses.");
    Serial.println();
    Bus2.reset_search();
    return;
  }

  Serial.print("ROM =");
  for ( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
    strROM += String(addr[i], HEX);
  }
  if (OneWire::crc8(addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return;
  }
  Serial.println();

  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      strChip = "DS18S20";
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      strChip = "DS18B20";
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      strChip = "DS1822";
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      strChip = "Device is not a DS18x20 family device";
      return;
  }

  Bus2.reset();
  Bus2.select(addr);
  Bus2.write(0x44, 1);        // start conversion, with parasite power on at the end

  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.

  present = Bus2.reset();
  Bus2.select(addr);
  Bus2.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  strData = String(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = Bus2.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
    strData +=  "-" + String(data[i], HEX);
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  strCRC = String(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  }
  else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;

  Serial.print("  Temperature = ");
  Serial.print(celsius);
  char buffer[12];
  String tempStr = dtostrf(celsius, 6, 2, buffer);
  tempStr.trim();
  Serial.print(" Celsius");
  Serial.println();
  Serial.println();
  //In DB speichern
  post("strROM=" + strROM + "&strChip=" + strChip + "&strData=" + strData + "&strCRC=" + strCRC + "&strTemperatur=" + tempStr + "&Send=Eintragen", strZielTemperatur);
  Serial.println("Bus2: Temperaturen in Datenbank speichern: " + strROM + " = " + tempStr);
  Serial.println();
}

void post(String Nachricht, String Ziel) {
  if (client2.connect("192.168.1.11", 82))  {
    client2.println("POST " + Ziel + " HTTP/1.1");
    client2.println("Host: 192.168.1.11");
    client2.println("Content-Type: application/x-www-form-urlencoded");
    client2.println("Connection: close");
    client2.println("User-Agent: Arduino/1.0");
    client2.print("Content-Length: ");
    client2.println(Nachricht.length());
    client2.println();
    client2.print(Nachricht);
    client2.println();
    client2.stop();
  }
}

Durchlesen, verstehen, fertig.

http://playground.arduino.cc/Learning/Memory

Dein Problem sind ua. die Serial.print Ausgaben.

adinist:
Mein Programm ist mittlerweile so angewachsen, dass der interne Flashspeicher zu klein dafür ist.

Sicher?
Ich würde eher vermuten, dass der Datenspeicher/RAM nicht reicht.

Hallo,

wie schon gesagt wurde, nutze für serielle Ausgabe das F Makro. Funktioniert aber nur mit bekannten Text. Nicht mit Variablen.

Und warum schleppst Du das Sensor Such Code Gedöhns mit dir rum?
Weist Du welcher Sensor wo sitzt? Dir fehlt eigentlich eine feste Zuordnung der Sensoren. Also welcher was/wo ist. Ich würde mir die Sensoradressen rausschreiben und dann nur mit festen Adressen arbeiten. Dann kann man die auch vertauschen und weis immer noch welcher Sensor was ist.

 // ***  Funktionen  *** 
int freeRAM() 
  { 
   extern int __heap_start, *__brkval;  
   int v;  
   return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
  }

und in der loop schreibste am Ende rein:

Serial.println(freeRAM());

Da guckste mal jetzt wie es ist und dann nochmal wenn Du alles was geht mit F Makro versehen hast.

Ich würde eher vermuten, dass der Datenspeicher/RAM nicht reicht.

Hier mein Fehler beim Kompilieren:

Der Sketch verwendet 33.872 Bytes (105%) des Programmspeicherplatzes. Das Maximum sind 32.256 Bytes.

Globale Variablen verwenden 2.120 Bytes (103%) des dynamischen Speichers, -72 Bytes für lokale Variablen verbleiben. Das Maximum sind 2.048 Bytes.

Und warum schleppst Du das Sensor Such Code Gedöhns mit dir rum?

Ich weiss genau welcher Sensor wo sitzt. Mein Code habe ich aus dem Onewire Beispiel genommen und für mich angepasst. Als Anfänger war ich natürlich mit dem Resultat mehr als zufrieden.
Und da ich sowieso alle Sensoren in regelmässigen Abständen auslesen möchte, dachte ich, dass es so eine gute Lösung ist.

Gezieltes Auslesen ist natürlich optimaler. Habe dafür leider kein Beispiel gefunden.

F Makro werde ich gerne testen, sobald ich den Sketch wieder kompilieren und hochladen kann.
Dazu muss der Sketch kleiner werden, oder der Speicher grösser (SD Auslagerung) :slight_smile:

Nein, eine SD Auslagerung geht so nicht. Wende das F-Makro richtig an, und du kannst kompilieren.

Er ist aber auch mit dem Flash am Ende!

Auf die String Klasse zu verzichten würde etwas sparen, aber selbst wenn das reicht, wäre das immer noch sehr knapp. Hier wäre wohl ein Mega ratsam.

sschultewolter:
Wende das F-Makro richtig an, und du kannst kompilieren.

Ich habe das F-Makro nun eingebaut. Der Sketch wird nun noch grösser (ist ja auch logisch).
Somit: F-Makro löst nicht mein primäres Problem, dass der Speicherplatz nicht für den Sketch ausreicht.

Zur Zeit versuche ich das Auslesen umzubauen, so dass die Sensoren gezielt ausgelesen werden.

Mal schauen wie es weiter geht...