Arduino Uno - Speicher zu klein?

Guten Tag zusammen,

ich betreibe an meinem Arduino Uno eine kleine Wetterstation. Es werden Sensoren ausgelesen und die Werte auf einem LCD-Display ausgegeben. Außerdem kommt ein Ethernet Shield zum Einsatz (W510).

Einzeln funktioniert alles ganz gut soweit. Auch kann ich alle Sensoren (PT100 Messwandler, DHT22, Anemometer) und das Display simultan betreiben. Sobald aber das Ethernet Shield dazu kommt, wird der Code nicht mehr richtig ausgeführt.

Sensoren + Display: Alles top

Sensoren + Display + Ethernet: Weder Display- noch Browserfunktion

PT100 Messwandler + Display + Ethernet: Display läuft, Browser läuft sporadisch. Wenn ich zunächst den Sensorcode auskommentiere und die Seite lade, kann ich sogar mit dem Messwandler- und dem Anemometercode die Seite erneut laden.

DHT22 + Ethernet: Seite lädt nicht.

Für mich sieht das nach einem Speicherproblem aus. Der Compiler sagt auch, dass der Speicher zu 85 % voll ist.

Was meint ihr? Vielen Dank schon mal im Voraus.

#include "DHT.h"
#include "Wire.h"
#include "LiquidCrystal_I2C.h"
#include "SPI.h"
#include "Ethernet.h"
#include "SD.h"

#define DHTPIN 9
#define DHTTYPE DHT22
#define BACKLIGHT_PIN 7

byte mac[]={0xBA, 0x86, 0x57, 0x2D, 0x90, 0x55};
IPAddress ip(192,168,0,24);
EthernetServer server(80);

LiquidCrystal_I2C lcd(0x27);

DHT dht(DHTPIN, DHTTYPE);

//Temperatur
float temperatur=0.0;

//Feuchtigkeit
float feuchtigkeit=0.0;

//Windgeschwindigkeit
const int m_time = 5;
int wind_ct = 0;
float windgeschwindigkeit = 0.0;
unsigned long time = 0;

//Windrichtung
int cny1, cny2, cny3;
String windrichtung;
String wr;

// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   50

File Datei;
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer

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

  Ethernet.begin(mac, ip);
  server.begin();

  dht.begin();
  time = millis();

  //Digitale Eingänge zum Messen der Windrichtung
  pinMode(1, INPUT);
  pinMode(2, INPUT);
  pinMode(3, INPUT);

  pinMode(BACKLIGHT_PIN, OUTPUT);
  digitalWrite(BACKLIGHT_PIN, HIGH);
  lcd.begin(16,2);

  //Abfrage ob SD-Karte gefunden wird
  if(!SD.begin(4)) {
    Serial.println("SD-Karte nicht gefunden!");
  }

  //Abfrage, ob Datei gefunden wird
  if(!SD.exists("index.htm")) {
    Serial.println("Datei nicht gefunden!");
  }
}

void loop() {
  messen_Temperatur();
  messen_Feuchtigkeit();
  messen_Geschwindigkeit();
  //messen_Windrichtung();

  //webBrowser();
  Display_Ausgabe();
}

void webBrowser(){
  EthernetClient client = server.available();

  if(client) {
    boolean currentLineIsBlank = true;
    while(client.connected()) {
      if(client.available()) {
        char c = client.read();
        if(req_index < (REQ_BUF_SZ - 1)) {
          HTTP_req[req_index] = c;
          req_index++;
        }
        if(c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          if(StrContains(HTTP_req, "ajax_inputs")) {
            client.println("Content-Type: text/xml");
            //client.println("Connection: keep-alive");
            client.println("Connection: close");
            client.println();
            XML_response(client);
          }
          else {
            client.println("Content-Type: text/html");
            //client.println("Connection: keep-alive");
            client.println("Connection: close");
            client.println();
            Datei = SD.open("index.htm");
            if(Datei) {
              while(Datei.available()) {
                client.write(Datei.read());
              }
              Datei.close();
            }
          }
          Serial.print(HTTP_req);
          req_index = 0;
          StrClear(HTTP_req, REQ_BUF_SZ);
          break;
        }
        if(c == '\n') {
          currentLineIsBlank = true;
        }
        else if(c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
  }
}

void XML_response(EthernetClient cl){
  cl.print("<?xml version = \"1.0\" ?>");
  cl.print("<inputs>");

  cl.print("<analog>");
  cl.print(temperatur);
  cl.print("</analog>");

  cl.print("<analog>");
  cl.print(feuchtigkeit);
  cl.print("</analog>");

  cl.print("<analog>");
  cl.print(windgeschwindigkeit);
  cl.print("</analog>");

  cl.print("<analog>");
  cl.print(windrichtung);
  cl.print("</analog>");

  cl.print("</inputs>");
}

void StrClear(char *str, char length){
  for(int i = 0; i < length; i++) {
    str[i] = 0;
  }
}

char StrContains(char *str, char *sfind){
  char found = 0;
  char index = 0;
  char len;

  len = strlen(str);

  if(strlen(sfind) > len) {
    return 0;
  }
  while(index < len) {
    if(str[index] == sfind[found]) {
      found++;
      if(strlen(sfind) == found) {
        return 1;
      }
    }
    else {
      found = 0;
    }
    index++;
  }
  return 0;
}

//Temperatur messen
void messen_Temperatur(){
  temperatur = (analogRead(A3)*(70.0/1023.0))-20.0;
}

//Luftfeuchtigkeit messen
void messen_Feuchtigkeit(){
  feuchtigkeit = dht.readHumidity();

  if (isnan(feuchtigkeit)){
    feuchtigkeit = 0;
  }
}

void Anemometer(){
  wind_ct ++;
}

//Windgeschwindigkeit messen
void messen_Geschwindigkeit(){
  wind_ct=0;
  time = millis();
  attachInterrupt(1, Anemometer, RISING);
  delay(1000*m_time);
  detachInterrupt(1);
  windgeschwindigkeit = (float)wind_ct / (float)m_time*2.4;
}

//Windrichtung messen
void messen_Windrichtung(){
  cny1 = digitalRead(0);
  cny2 = digitalRead(1);
  cny3 = digitalRead(2);

  if(cny1 == LOW && cny2 == LOW && cny3 == LOW){
    windrichtung = "Norden";
    wr = "N";
  }
  else if(cny1 == HIGH && cny2 == LOW && cny3 == LOW){
    windrichtung = "Nord-Osten";
    wr = "NO";
  }
  else if(cny1 == LOW && cny2 == HIGH && cny3 == LOW){
    windrichtung = "Osten";
    wr = "O";
  }
  else if(cny1 == HIGH && cny2 == HIGH && cny3 == LOW){
    windrichtung = "Süd-Osten";
    wr = "SO";
  }
  else if(cny1 == LOW && cny2 == LOW && cny3 == HIGH){
    windrichtung = "Süden";
    wr = "S";
  }
  else if(cny1 == HIGH && cny2 == LOW && cny3 == HIGH){
    windrichtung = "Süd-Westen";
    wr = "SW";
  }
  else if(cny1 == LOW && cny2 == HIGH && cny3 == HIGH){
    windrichtung = "Westen";
    wr = "W";
  }
  else {
    windrichtung = "Nord-Westen";
    wr = "NW";
  }
}

//Werte auf LCD-Display ausgeben
void Display_Ausgabe(){
  lcd.clear();

  lcd.setCursor(0,0);
  lcd.print(temperatur);
  lcd.print(" C");

  lcd.setCursor(0,1);
  lcd.print(feuchtigkeit);
  lcd.print(" %");

  delay(5000);
  lcd.clear();

  lcd.setCursor(0,0);
  if(windgeschwindigkeit <10){
    lcd.print("0");
  }
  lcd.print(windgeschwindigkeit);
  lcd.print(" km/h");

//  lcd.setCursor(0,1);
//  lcd.print(wr);
}

Ich kann mir nicht so recht vorstellen, wie eine komplette Webseite in den kleinen Speicher des Uno geladen werden kann, und dann auch noch in einem Browser angezeigt werden soll. Wie groß soll die Webseite denn sein, und wieviel Speicher ist dafür übrig?

Der Compiler sagt auch, dass der Speicher zu 85 % voll ist.

Der Compiler nennt dir zwei Zahlen.
Flash (Programm-) Speicher kannst du zu 100% füllen, RAM (globale Variablen) bei weitem nicht.

String Objekte sind ganz schlecht und überflüssig.

Nachtrag: Und bei festen Texten kann man auch sparen mit dem F-Makro:
cl.print(F("")); // Spart 8 byte RAM

Nachtrag2:Ohne SD hast du auf dem Uno min. 25% mehr RAM zur Verfügung.
Mach evtl. mal eine minimale index.htm per F-Makro,
schmeiss den String windrichtung ersatzlos raus und mach String wr zu

const char* wr;

Ausserdem passtSerial.begin(9600);nicht zu

  cny1 = digitalRead(0);
  cny2 = digitalRead(1);

Hallo,

Wenn ich das richtig verstehe, willst Du Daten aus zwei (oder mehr) Sensoren auslesen, die auf einer SD Karte speichern und gleichzeit auf einer Website sichtbar machen.

Wenn die Website das wichtigste ist, dann würde ich die Website auf einen Server (kann auch ein Raspi oder YUN sein) laden und den Arduino anweisen die Daten an den Server zu schicken.

Wenn du aber die Daten auf der SD Card speicherst um dann aus den Daten auf der SD Karte eine HTML Seite zu generieren... naja... das wird sich speichermäßig nicht auf einem Arduino Uno ausgehen.

Oder willst Du eine Website die einfach nur die aktuelle Temperatur anzeigt?

lg dony

Wenn ich das richtig verstehe, dient die SD Karte nur dazu, eine feste index.htm bereitzustellen.
Dort sind vermutlich Links drin, die eine json Anfrage auslösen, die dann in der Funktion XML_response beantwortet werden.

Wenn es nur um eine index.html geht, dann verbraucht wie michael_x bereits sagt das ganze SD zeug mehr speicher als die HTML Seite. Ich glaube es ist fast so wie michael_x in #4 gesagt hat.
Das sind aber nur Annahmen und Vermutungen, deshalb bin ich hier vorerst raus :wink:

lg dony

Ups...hab ich vergessen zu erwähnen.

Auf der SD Karte ist nur die index.htm, in der 3 Anzeigen erstellt werden. Ansonsten gibt es dort nur Textausgaben. Die jeweiligen Werte werden dann vom Sketch direkt per XML_Response an den Browser übergeben.

Der Programmspeicher ist zu 80 %, der RAM-Speicher zu 89 % voll... :smiley:

Die Anmerkungen bzgl. String und F-Makro senken die Speicher auf 77 % und 75 %.
Uuuuuund....es klappt. Ich kann nun alles simultan betreiben. Super, vielen Dank. :slight_smile:

Ohne SD-Karte und das Programm entsprechend angepasst, gehen die Speicher auf 50 % und 39 % runter. Allerdings ist das ein Schulprojekt und Vorgabe ist es die SD zu verwenden...

Die letzte Anmerkung bzgl. Serial.begin() und digitalRead() verstehe ich allerdings nicht. Wieso?

t1msen:
Die letzte Anmerkung bzgl. Serial.begin() und digitalRead() verstehe ich allerdings nicht. Wieso?

Die Serielle Kommunikation läuft über die Pins 0 und 1 und sollten daher nicht benutzt werden (außer mit absicht).
Irgendwo in deinem Sketch hab ich pinMode(1, INPUT); gefunden, wäre mir aber ohne hinweis von michael_x auch nicht aufgefallen, hab den Sketch nur überflogen.
edit: Hab die entsprechenden Zeilen gefunden, pin 0 wird ausgelesen aber es gibt kein pinMode() dazu.

lg dony

Oh. Wieder was gelernt.

Sowohl bei pinMode() als auch bei digitalRead() sollten es eigtl. Pin 0-2 sein. Naja, die entsprechenden Sensoren waren sowieso noch nicht angeschlossen. Habs jetzt mal auf Pin 5-7 geändert.

t1msen:
Habs jetzt mal auf Pin 5-7 geändert.

Das ist gut.

Aber Du musst da schon sicher gehen. Du verwendest den I2C und den SPI Bus. Diese Pins darfst du natürlich alle nicht verwenden. Und du verwendest das Ethernet Shield, da musst du nachschauen welche Pins vom Shield verwendet werden, diese darfst Du dann natürlich auch nicht verwenden.

Such mal nach Arduino Pinout (Bilder), da sind alle Pins erklärt. Das Ethernet Shield wird auch auf dieser Seite erklärt.

lg dony
edit: Ich hab jetzt kurz selbst die Pin Belegung des Ethernet Shield gegoogelt. Du hast mit Deiner Pin Belegung Glück gehabt, die Beteonung liegt auf Glück. :wink:

Danke.

Das Pin A4 und A5 vom i²C, sowie Pin 4 und 10-13 vom Ethernet Shield reserviert sind, war mir allerdings bewusst. :smiley:

war mir allerdings bewusst :smiley:

Glück hat auf Dauer nur der Tüchtige.
Glückwunsch und Danke für die Rückmeldung :wink:

Da du ja ein I2C - LCD dran hast, kannst du Serial auch weglassen und die Fehlermeldungen, dass die Karte fehlt, dort ausgeben. Hast dann Pin 0 und 1 zur Not frei.

"Programmspeicher 80 %" ist schonmal was, für ein kleines Schulprojekt. Meine Anerkennung.

Wo kommt übrigens der 3-bit Windrichtungsgeber her? Hast du da einen Link?

Danke. :smiley:

Ja, stimmt. Das geht natürlich auch zur Not.
Naja, ich denke das kann man alles noch 'nen bisschen ressourcensparender und "vernünftiger" programmieren. Bin aber halt 'nen totaler Laie. :stuck_out_tongue:

#Edit:
Hab mich verlesen...
Die Wetterfahne wird selbst gebastelt und anschließend über Graycodescheibe und drei CNY70 die Windrichtung "bestimmt".

t1msen:
Das Pin A4 und A5 vom i²C, sowie Pin 4 und 10-13 vom Ethernet Shield reserviert sind, war mir allerdings bewusst. :smiley:

Sehr Gut :wink: Wenn Du jetzt noch weißt welcher Pin was macht...

Musst du einen DHT22 verwenden oder hast du zufällig einen DHT11 rumliegen, den der läßt sich 'einfacher' ansprechen (ohne lib) was platzsparend ist <-edit: zwar ohne I2C aber Du hast ja noch das Display am I2C Bus.

Wenn du Pin 0,1 verwenden willst, ist das natürlich möglich, wie michael_x bereits sagte. Allerdings darf kein Seriel.begin() im Sketch stehen. Außerdem sollte man bedenken, das bei jedem Sketch Upload die Pins verwendet werden (egal, was im Sketch steht), da der µC so das Programm empfängt. -> Besser keine Kabeln an diese Pins, wenn ein Upload bevor steht.

lg dony

Die Wetterfahne wird selbst gebastelt und anschließend über Graycodescheibe und drei CNY70 die Windrichtung "bestimmt".

Gute Idee!

Naja, ich denke das kann man alles noch 'nen bisschen ressourcensparender und "vernünftiger" programmieren. Bin aber halt 'nen totaler Laie. :stuck_out_tongue:

Ja schon. Der übliche Engpass beim Uno (nach den Pins) ist der RAM.
Wenn du im Flash noch 20% Luft hast, dient das Optimieren dort eher zu Lernzwecken oder sportlichem Ehrgeiz. Ob du deinen Gray-Decoder nun mit einem Sack voll if oder einer Tabelle machst, ist bei 3 Bit nicht wirklich wesentlich. Hauptsache du machst es selbst und lernst so was. Oder du lernst was beim Suchen, Verstehen, Anpassen gefundener Funktionen.