Code schlanker machen - Char Array statt String?

finalerCode5.ino (7,5 KB)

Mein Arduino Uno kommt mit diesem Code nun an seine Grenzen. Ich bin noch Programmieranfänger und komme nach einigem rum probieren leider noch immer nicht darauf wie ich die viel Speicher schluckenden Strings umgehen kann.
Hatte auch überlegt einfach auf ein Arduino Mega umzusteigen, jedoch kann ich für diesen keinen Data Logging Shield finden.

Ich hoffe jemand hat eine Idee für mich.

Liebe Grüße.
Crispin

Hallo crisp1n0

Herzlich Willkommen im besten Arduinoforum der Welt.

Guck mal hier:

Danke, jedoch nutze ich den Flash-Speicher bereits und bin immer noch am Limit.

Na sowas

Im nächsten Schritt die korrekte Verwendung der Datentypen überprüfen, insbesondere bei array´s und Co.

Hallo @crisp1n0
Lies doch bitte hier wie man Code postet. Dann kann den Sketch jeder lesen und du hättest mehr Helfer.

if ((timer + LCDzeit * 1000) < millis())
Das ist an 2 Stellen falsch, die anderen sind ok.


void get_time() { //Read Time from RTC
  DateTime now = rtc.now();
  timestring = now.day();
  timestring += "-";
  timestring += now.month();
  timestring += "-";
  timestring += now.year();
  timestring += " ";
  timestring += now.hour();
  timestring += ":";
  timestring += now.minute();
  timestring += ":";
  timestring += now.second();
  Serial.println(timestring);
}

Das geht auch ohne String.

"Data Logging" riecht nach SD-Karte und da brauchst du den halben RAM eines Uno/Nano schon, nur um eine Datei zu öffnen.
Da sind String Objekte ein völlig unnötiger Luxus.

Auf die Schnelle zusammengestoppelt:

#include <Streaming.h> // die Lib findest du selber ;-)


struct RTC {
  int   day()    {return 10;}
  int   month()  {return 20;}
  int   year()   {return 30;}  
  int   hour()   {return 40;}
  int   minute() {return 50;}
  int   second() {return 60;}
};
RTC rtc;

Print &operator <<(Print &p, RTC &now)
{
 p <<  now.day()
   <<  "-" <<   now.month() 
   <<  "-" <<   now.year() 
   <<  " " <<   now.hour()
   <<  ":" <<   now.minute()
   <<  ":" <<   now.second() ;
 return p;
}


void setup() 
{
  Serial.begin(9600);
  Serial << F("Start: ") << F(__FILE__) << endl;

  
  Serial     << rtc << endl;
 // lcd        << rtc << endl;
 // dataFile   << rtc << endl;
}

void loop() 
{

}

Besser 59 oder 00?

Gruß Tommy

Jain!
Das ist eine reine Mock Struktur, Platzhalter.
Also recht wurscht was da drin steht.
Das mocken macht auch mit falschen Daten Sinn, um logische Fehler zu finden.

Hallo,

warum im struct keine einfachen Variablen?
Wofür überhaupt ein return Parameter?

struct RTC {
  int   day;
  int   month;
  int   year;
  int   hour;
  int   minute;
  int   second;
};

Danke für die vielen super Ratschläge.
Zunächst verstehe ich nicht was an

  if ((timer + LCDzeit * 1000) < millis()) {
    timer = millis(); />

verkehrt ist, da die Funktion das tut was sie soll..?

tatsächlich finde ich die steaming.h lib nicht :frowning: also ich finde sie nur in githup als Teil der glcd Libary die ich jedoch, obwohl ich mich angemelded habe nicht downloaden kann. Reicht die Freeversion dafür nicht?

Hier nochmal der Komplette Code:

#include <dht.h>
#include <Bounce2.h>
#include <RTClib.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <MQ135.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);

//=============================================================================

#define DHT1_PIN 7
#define DHT2_PIN 8
#define DHT3_PIN 9
#define MQ135_PIN A1

#define RelaisTemp_PIN 6
#define RelaisHumidity_PIN 5
#define RelaisLuft_PIN 3
#define RelaisMagnetventil_PIN 4

#define startTemp 20
#define DHT_min_Temp 20
#define DHT_max_Temp 35
#define startHumidity 70
#define DHT_min_Humidity 70
#define DHT_max_Humidity 95
#define startMaxTemp 25
#define maxMaxTemp 35

#define knopfHumidity 2
#define knopfTemp 18
#define knopfFan 17
#define pinup 16



//=============================================================================
dht DHT1;
dht DHT2;
dht DHT3;
int soll_Temp, soll_Humidity, max_Temp, GasLevel = 0, messwertBoden = 0, count = 1;
unsigned long Luft_Zeit, timer, altzeit, LCDzeit = 10, sdzeit = 5, relaisZeit = 30;
const int chipSelect = 10, RelaisLuftStatus = LOW, BodenfeuchteLevelLow = 832, BodenfeuchteLevelHigh = 382, GasLevelLow = 50, GasLevelHigh = 500;
String timestring, mvalue1, mvalue2, mvalue3, mvalue4, mvalue5, mvalue6, mvalue7, mvalue8, mvalue9, mvalue10;
int prozentG = map(GasLevel, GasLevelHigh, GasLevelLow, 100, 0);
int prozentB = map(messwertBoden, BodenfeuchteLevelHigh, BodenfeuchteLevelLow, 100, 0);
 
Bounce bHumidity = Bounce();
Bounce bTemp = Bounce();
Bounce bFan = Bounce();
Bounce b = Bounce();

RTC_DS1307 rtc;
MQ135 mq135_sensor(MQ135_PIN);

void setup() {

  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  Serial.println(F("Initializing SD card..."));
  if (!SD.begin(chipSelect)) {
    Serial.println(F("SD Card error"));
    return;
  }
  Serial.println(F("card initialized"));
  if (! rtc.begin()) {
    Serial.println(F("No RTC found"));
  } else {
    Serial.println(F("RTC clock found"));
  }
  if (! rtc.isrunning()) {
    Serial.println(F("RTC is not configured"));
  }

  bHumidity.attach(knopfHumidity, INPUT_PULLUP);
  bHumidity.interval(5);
  bTemp.attach(knopfTemp, INPUT_PULLUP);
  bTemp.interval(5);
  bFan.attach(knopfFan, INPUT_PULLUP);
  bFan.interval(5);
  b.attach(pinup, INPUT_PULLUP);
  b.interval(5);
  
  pinMode(RelaisTemp_PIN, OUTPUT);
  digitalWrite(RelaisTemp_PIN, HIGH);
  pinMode(RelaisHumidity_PIN, OUTPUT);
  digitalWrite(RelaisHumidity_PIN, HIGH);
  pinMode(RelaisLuft_PIN, OUTPUT);
  digitalWrite(RelaisLuft_PIN, HIGH);
  pinMode(RelaisMagnetventil_PIN, OUTPUT);
  digitalWrite(RelaisMagnetventil_PIN, HIGH);

}


void loop() {

  DHT1.read22(DHT1_PIN);
  DHT2.read22(DHT2_PIN);
  DHT3.read22(DHT3_PIN);
  if (soll_Humidity == 0) {
    soll_Humidity = startHumidity;
  }
  if (soll_Temp == 0) {
    soll_Temp = startTemp;
  }
  bHumidity.update();
  if (bHumidity.fell()) {
    soll_Humidity = soll_Humidity + 5;
  }
  if (soll_Humidity > DHT_max_Humidity) {
    soll_Humidity = DHT_min_Humidity;
  }
 
  bTemp.update();
  if (bTemp.fell()) {
    soll_Temp = soll_Temp + 1;
  }
  if (soll_Temp > DHT_max_Temp) {
    soll_Temp = DHT_min_Temp;
  }
  
  if (millis() - Luft_Zeit > 900000 ) {
    digitalWrite(RelaisLuft_PIN , LOW);
    Luft_Zeit = millis();
  }
  else {
    if (millis() - Luft_Zeit > 60000 ) {
      digitalWrite(RelaisLuft_PIN, HIGH);
    }
  }
  
  if ((timer + relaisZeit * 1000) < millis()) {
    timer = millis();
      if ((DHT1.temperature) > soll_Temp) {
        digitalWrite(RelaisTemp_PIN, HIGH);
        }
        else {
        digitalWrite(RelaisTemp_PIN, LOW);
        }
          if ((DHT1.humidity) > soll_Humidity) {
          digitalWrite(RelaisHumidity_PIN, HIGH);
          }
          else {
          digitalWrite(RelaisHumidity_PIN, LOW);
          }

            if ((DHT1.humidity) > soll_Humidity) {
            digitalWrite(RelaisMagnetventil_PIN, HIGH);
            }
            else {
            digitalWrite(RelaisMagnetventil_PIN, LOW);
            }
  }
  b.update();
  if (b.fell()) {
    count++;
    if (count > 2) count = 1;
    altzeit = 0;
  }
  if ((timer + LCDzeit * 1000) < millis()) {
    timer = millis();
    switch (count) {
      case 1:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(F("Temperatur:"));
        lcd.print(DHT1.temperature);
        lcd.print(F("\337C "));
        lcd.setCursor(1, 0);
        lcd.print(F("Soll:"));
        lcd.print(soll_Temp);
        lcd.print(F("\337C "));
        lcd.setCursor(0, 1);
        lcd.print(F("Luftfeuchte:"));
        lcd.print(DHT1.humidity);
        lcd.print(F("\%"));
        lcd.setCursor(1, 1);
        lcd.print(F("Soll:"));
        lcd.print(soll_Humidity);
        lcd.print(F("\%"));
        lcd.setCursor(0, 2);
        lcd.print(F("Substratfeuchte:"));
        messwertBoden = analogRead(A0);
        lcd.print(prozentB);
        lcd.print(F("\%"));
        lcd.setCursor(0, 3);
        lcd.print(F("CO2-Level:"));
        GasLevel = analogRead(A2);
        lcd.print(prozentG);
        lcd.print(F("\%"));
        break;
      case 2:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(F("Temperatur:"));
        lcd.print(DHT2.temperature);
        lcd.print(F("\337C "));
        lcd.setCursor(1, 0);
        lcd.print(F("Soll:"));
        lcd.print(soll_Temp);
        lcd.print(F("\337C "));
        lcd.setCursor(0, 1);
        lcd.print(F("Luftfeuchte:"));
        lcd.print(DHT2.humidity);
        lcd.print(F("\%"));
        lcd.setCursor(1, 1);
        lcd.print(F("Soll:"));
        lcd.print(soll_Humidity);
        lcd.print(F("\%")); 
        lcd.setCursor(0,2);
        lcd.print(F("Draußen:"));
        lcd.print(DHT3.temperature);
        lcd.print(F("\337C "));
        lcd.print(DHT3.humidity);  
        lcd.print(F("\%"));  
        lcd.setCursor(0,3);
        lcd.print(F("MaxTemp:"));
        lcd.print(max_Temp);
    }
  }
  if ((timer + sdzeit * 1000) < millis()) {
    timer = millis();
    get_logvalue();
    get_time();
    write_data();
  }
}

void get_logvalue() {

  mvalue1 = (DHT1.temperature); 
  mvalue2 = (DHT1.humidity);
  mvalue3 = (DHT2.temperature);
  mvalue4 = (DHT2.humidity);
  mvalue5 = (prozentB);
  mvalue6 = (prozentG);
  mvalue7 = (DHT3.temperature);
  mvalue8 = (DHT3.humidity);
  mvalue9 = soll_Temp;
  mvalue10 = soll_Humidity;
}

void get_time() { //Read Time from RTC
  DateTime now = rtc.now();
  timestring = now.day();
  timestring += "-";
  timestring += now.month();
  timestring += "-";
  timestring += now.year();
  timestring += " ";
  timestring += now.hour();
  timestring += ":";
  timestring += now.minute();
  timestring += ":";
  timestring += now.second();
  Serial.println(timestring);
}

void write_data() { //Write to SD card
  String dataString = "SollTemp:" + mvalue9 + " SollHumidity: " + mvalue10 + "  " + "Klima links: " + mvalue1 + " " + mvalue2  + "  " +  "Klima rechts: " + mvalue3 + " " + mvalue4 + "  " + "KlimaAußen: " + mvalue7 + " " + mvalue8 + "  " + "Bodenfeuchte: " + mvalue5 + "   " + "CO2-Level: " + mvalue6;

  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(timestring);
    dataFile.println(dataString);
    dataFile.close();
    Serial.println(timestring);
    Serial.println(dataString);
  
  }
  else {
    Serial.println(F("error writing datalog.txt"));
  }
}

Nach ca 49 Tagen fällt dir das auf die Füße.
Die Addition ist falsch.
So wird der Millis Überlauf nicht kompensiert.

Zum Thema: [Bericht] Der (Millis) Ueberlauf im Test

Wie schon gesagt: An anderen Orten machst du es richtig!
Dort wird subtrahiert.

Ich fand das ganz interessant:

Und

1 Like

Wenn man die überflüssigen String-Variablen und Funktionen löscht, reduziert sich bei mir der Programmspeicherplatz von 71% auf 65% und der dynamische Speicher von 68% auf 60%.

void write_data() { //Write to SD card
  DateTime now = rtc.now();
  char buf[200];
  snprintf_P( buf, sizeof(buf), PSTR("%02d-%02d-%4d %02d:%02d:%02d\nSollTemp: %3.1f SollHumidity: %3d  Klima links: %3.1f %3.1f   Klima rechts: %3.1f %3.1f   KlimaAußen: %3.1f %3.1f   Bodenfeuchte: %3d   CO2 - Level: %3d"),
            now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second(),
            double(DHT1.temperature), soll_Humidity, double(DHT1.temperature), double(DHT1.humidity), double(DHT2.temperature), double(DHT2.humidity), double(DHT3.temperature), double(DHT3.humidity), prozentB, prozentG );

  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(buf);
    dataFile.close();
    Serial.println(buf);

  }
  else {
    Serial.println(F("error writing datalog.txt"));
  }
}

Wobei da der Speicher den die Strings brauchen, nicht mitgezählt wird!

Weiß jetzt nicht genau, was Du meinst, String-Objekte oder Zeichenketten vom Typ char?

@crisp1n0: Die Übersicht der IDE zeigt nur den Bedarf an globalen Variablen an. In der Funktion write_data wird mit char buf[200]; nicht unerheblich viel Speicher lokal verwendet. Der muß zur Verfügung stehen, sonst crasht das Programm. Da der Speicher am Ende der Funktion auch wieder freigegeben wird, können andere Funktionen darauf zugreifen. Durch diese zeitlich versetze Mehrfachverwendung des Speichers steht dann mehr davon zur Verfügung, als würde man alle Variablen global verwalten wollen.

@michael_x: Wenn Du das gemeint hast, stimme ich Dir zu :slightly_smiling_face:

Ich bezog mich auf die Angabe beim Übersetzen : x Byte für globale Variable.
Da ist das lokale char-Array (das du erwähnst) nicht dabei, auch kein lokales String-Objekt.
Wenn du ein globales String Objekt definierst, belegt das nur 6 Byte im RAM. Der eigentliche Text des Strings wird auch nicht mitgezählt, da er erst zur Laufzeit dazukommt.

1 Like

Oh jaa da war was!! Hatte ich wieder ganz vergessen. Jetzt passen sie. Vielen Dank!

Weshalb schribst du hier noch einmal byte mit in die Funktion?

  if(byte(jetztzeit - zeitmerker) >= interval) // der wahre Jakob