Problem combining dht-22 sensor with microSD module

Hello,
I am building a "GrainBrain" that helps a small farm monitor and control a grain drying bin. My programming skills are remedial, please forgive me!
When I added the section of code to write data to the microSD card, the program compiled but did not run, instead appeared to get "stuck" after a few loops.
If I comment out the microSD card part, the program works, printing to the serial monitor and LCD.
If I uncomment the microSD section, and comment out the code section that reads the values from the dht-22 sensors, the code runs well, printing simply zeros to the microSD card!
The program only needs to check the sensors every 5 minutes, and only needs to write data to the micro SD every 10 minutes.
I attempted to use an attachInterrupt function for the microSD part of the code that uses a timer. It sort of worked, in the sense that the program ran and didn't get stuck, but failed to open the file and write the data. I also attempted to use a simple timer and/or a "loop counter" to "stagger" the sensor and the microSD parts of the code, but I just couldn't get it to work, probably because of lack of programming ability?
I very much appreciate any suggestions! Thank you.


```cpp
/*
*This sketch combines a arduino nano with a DS3231 clock module, two DHT-22 temperature/humidity sensors, and a micro-sd card module
*It also allows as input the type of grain being dried (as reference to a look-up table of equilibrium Moisture Contents) 
*And allows as input the latest measured MC as inputed with a potentiometer by the farmer
*Remember, to first set time and date on ds3231 use DS3231Serial_Easy_Lu
*/

//============================================================================BEGIN DECLARATIONS===========================================
#include <DS3231.h> //includes the library for using the DS3231 Time module
DS3231  rtc(SDA, SCL); //sets up the time module

#include <LiquidCrystal_I2C.h> //includes the library for using the 2x16 display with the built in I2C adapter
LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20 column and 4 row

#include "DHT.h" //library for dht sensors
#define DHTPIN_inlet 2 // first dht22 input pin
#define DHTPIN_outlet 3 // second dht22 input pin
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
DHT dht_inlet(DHTPIN_inlet, DHTTYPE);
DHT dht_outlet(DHTPIN_outlet, DHTTYPE);



float inletTemp; //these 4 variables will store the temperatures (in Celcius!) and humidities (%rH) from the sensors
float inletHum;
float outletTemp;
float outletHum;

#include <SPI.h>
#include <SD.h> //library for microSD card
const int chipSelect = 10;// sets pin 10 for CS on the microSD module pinout
File myFile; //creates a file for the micro SD card

int potPin = A0; //declare the potentiometer pin A0
int potVolt = 0; // will be the voltage of the dimmer (0-5 volts), read as a integer from 0-1023
float grainMCx2 = 0; //a variable for the entering of the grain moisture content times two to avoid the integer math problem of the mapping function
float grainMC = 0;  //a varialble for entering the grain moisture content with resolution of 0.5%

const int buttonPin = 4;    // the number of the pushbutton pin
// Variables for the button debouncer will change;   
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input
// the following variables for  the button debouncer are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 10;    // the debounce time; increase if the output flickers
char *grainType[7] = {"Corn", "Hard Wheat", "Soft Wheat","Barley", "Rye", "Sunflower"}; //this string array holds the different types of grain
//eventually there will be h files with the EMC charts for each of the above grains
int i = 0;


//============================================================END DECLARATIONS=======================================================


//=============================================================BEGIN SETUP==========================================================
void setup() { 

  Serial.begin(9600);


/*
  // wait for Serial Monitor to connect. Needed for native USB port boards only:
  while (!Serial);

  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("1. is a card inserted?");
    Serial.println("2. is your wiring correct?");
    Serial.println("3. did you change the chipSelect pin to match your shield or module?");
    Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!");
    while (true);
  }

  Serial.println("initialization done.");
  delay(20);
*/


 rtc.begin(); // Initialize the DS3231 realtimeclock object

  lcd.init(); // Initialize the lcd object, clear it, turn on the backlight, set the cursor (?)
  lcd.clear();
  lcd.backlight();
  lcd.setCursor(0,0);

  //initialize dht sensors
  dht_inlet.begin();
  dht_outlet.begin();

  pinMode (potPin, INPUT); //turn on potPin as input this will input the grain MC

 pinMode(buttonPin, INPUT);  //this is the input push button this will select type of grain




}
//====================================================================END SETUP============================================================



//=============================================================================MAIN LOOP================================================================
void loop() {

//===============================================SUBROUTINE TO READ TEMPERATURE AND HUMIDITY WITH DHT-22=============================================
      //read the temp and humidity sensors
   
   float inletHum = dht_inlet.readHumidity();
   float inletTemp = dht_inlet.readTemperature();//Celcius

   float outletHum = dht_outlet.readHumidity();
   float outletTemp = dht_outlet.readTemperature(); //Celciius
  

  
//=================================================END TEMPERATURE AND HUMIDITY SUBROUTINE===========================================================

// =================================================SUBROUTINE TO DETECT BUTTON PUSH AND UPDATE GRAIN TYPE SELECTION==================================

  // read the state of the button into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // move the grain type to the next position if the new button state is HIGH
      if (buttonState == HIGH) {
        i = i+1; //increments the grain type to the next word in the array 
        //clear the lcd line with the grain type (line 3)
        lcd.setCursor(0,2); //sets lcd cursor to collum one, row three
        lcd.print("                    "); //prints 20 blank spaces to clear row

        if (i==6){ //if the counter i gets to. 6 have it wrap around to zero...
          i=0;

        }
      }
    }
  }
  // save the button reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
  Serial.println(grainType[i]);

  //==============================================END BUTTON PUSH DETECTION SUBROUTINE==================================================



// ================================================SUBROUTINE TO READ POTENTIOMETER FOR LATEST GRAIN MC INPUT===============================================

  potVolt =  analogRead (potPin); //read the potentiometer voltage into the variable potVolt (0-1023), more resistance means lower voltage
  grainMCx2 = map(potVolt,1023,0,0,60); //creates the grainMC variable from the pot input allows grianMC from 0%-30%
  grainMC = grainMCx2/2;  //  to get the resolution to 0.5% it's necessary to change the integer (0-60) to a decimal (0-30 in increments of 0.5)
  Serial.print("moisture content:  ");
  Serial.println(grainMC);
// ========================================================END READ POTENTIOMETER SUBROUTINE===================================================



//==================================================SUBROUTINE TO DISPLAY INFO ON THE LCD SCREEN======================================================
// Now display the temperature and humidity on the lcd

 lcd.setCursor(0,0);
 lcd.print("In: ");
 lcd.print((inletTemp * 9.0) / 5.0 + 32.0);
 lcd.print("F--");
 lcd.print(inletHum);
 lcd.print ("%rH");

 lcd.setCursor(0,1);
 lcd.print("Out:");
 lcd.print((outletTemp * 9.0) / 5.0 +32);
 lcd.print("F--");
 lcd.print(outletHum);
 lcd.print("%rH");

 lcd.setCursor(0,2);
 lcd.print(grainType[i]);

 lcd.setCursor(0,3);
 lcd.print("grain MC: ");
 lcd.print(grainMC);

 delay(20);


//===================================================END LCD SUBROUTINE================================================================================

//=====================================================SUBROUTINE TO PRINT TO SERIAL MONITOR===========================================================
   // Send date to serial monitor
  Serial.print(rtc.getDateStr());
  Serial.print(" -- ");
  // Send time to serial monitor
  Serial.println(rtc.getTimeStr());

  	// Printing the temperature and humidity on the serial monitor

  Serial.print("Inlet Temperature F:  ");
  Serial.println(((inletTemp * 9.0) / 5.0 + 32.0));
  Serial.print("Inlet Humidity:  ");
  Serial.println(inletHum);
  
  Serial.print("Outlet Temperature F:  ");
  Serial.println(((outletTemp * 9.0) / 5.0 + 32.0));
  Serial.print("Outlet Humidity:  ");
  Serial.println(outletHum); 

  delay(50);
//=======================================================END SERIAL MONITOR SUBROUTINE===============================================

 /*
//=======================================================SUBROUTINE TO WRITE DATA TO MICROSD CARD=====================================


   //open file on micro-sd card and write data to file
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  //myFile = SD.open("TempRH.txt", FILE_WRITE);  //tempRH.txt is the file name, FILE_WRITE tells the computer that this is a write operation

    // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  //myFile = SD.open("test.txt", FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
  // with the file open, write the date, time, temp, and humidity separated by commas
    //myFile.print(rtc.getDateStr());
    //myFile.print(",");
    //myFile.print(rtc.getTimeStr());
    //myFile.print(",");
    //myFile.print((inletTemp * 9.0) / 5.0 + 32.0);
    //myFile.print(",");
    //myFile.println(inletHum);
    // close the file:
    //myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
  


//=======================================================END MICRO SD CARD SUBROUTINE=======================================================
 */
  delay(300); // 
}
//==========================================================END MAIN LOOP=================================================================

No, bad idea. Just use millis() as the timer.

Looks like you are running low on dynamic memory (ram), that could be a problem.

Try using the F() macro for all the print() statements with text literals (quoted text), this stores the text in program memory instead of using ram. Works with print() statements for the LCD and SD card in addition to Serial.

  Serial.print(F("Initializing SD card..."));

I don’t see where you have initialised the SD library? It appears to be within your commented section?

Thank you! That was it; when i added the F() to the print statements the whole thing started working again. I really appreciate your help; I never would have thought to check the RAM because upon compiling it cheerfully says only 62% of RAM is used, which make me think there's no problem. My CS friend patiently explained the fundamentals of computers, and now i get it. In case this is helpful to anyone else, my CS friend also says (i hope i get this correct) that if a local variable is established in a function, then when the function ends and the main loop resumes, the RAM for the local variable is liberated for use elsewhere in the program. Another possible way to free up RAM?

The messages only show the statically allocated RAM. The SD support dynamically allocates 512 bytes for a buffer that is not counted in the message. Graphic displays can allocate 1K or more. Neopixel displays dynamically allocate 3xnumber of LEDS. A lot of ways to run out of RAM :slightly_smiling_face: