Need help interfacing LCD with datalogger

Hello everyone. I hope I am posting this in the right area. Let me try and give some background on the project:

I am setting up a hydroponics monitoring system modeled very similarly to the one posted here:

Basically I have a variety of sensors (EC, PH, CO2, humidity, and temperature) that I want to use to take measurements, display the measurements on an LCD, and save the measurements in a data logger.

One key difference between what I am doing and what is shown in the above link is the presence of a data logger (DL from now on). As the title suggests, I am having trouble interfacing all of the sensors, LCD screen, and data logger with the Arduino Uno. I have some pictures that I will try to upload, but let me try to describe what my system looks like in case my picture are not clear enough.

Basically, I have a data logging shield that is placed on top of the Uno. At first I planned to place an LCD shield on top of the data logging shield so all the components are connected, but I believe this is an issue because both the data logger and the lcd use digital pin 10 (correct me if this is wrong!)

So, I disconnected the lcd from the uno+DL and tried to use a bunch of jumper wires to connect the relevant pins from the lcd to the uno+DL, except I connected pin 10 on the LCD to pin 4 on the uno+DL.

All of the sensors are connected to the LCD, and then I used more jumpers to connect the sensors pins from the lcd to the uno+DL.

In my head, this should 100% work, but I am having some issues:

  1. The LCD isn't displaying data like it should. Sometimes their is nothing but 'noise' on the display. Sometimes nothing is displayed. Sometimes the data is displayed for a few seconds, and then disappears. Sometimes the data stays, but doesn't update as the measurements change (its like the screen is frozen on the same values). Hopefully you get the idea.

  2. The logger isn't recording data that matches the measurements shown on the diplay (I tried using just the LCD+uno without the DL and the display seemed to be working as intended, so i have a general idea of what the data should look like.).

Does anyone have any suggestions? A few of my guesses:

I am accidentally using the same pin on multiple different things.
Could their be an issue with memory? Am i just asking my arduino to do too much?

CHeck out my code below. Hopefully you made it through all me text. Thank you in advance for your help.

links:
https://www.dfrobot.com/product-1084.html

I belive this is the data logger, but i am not completely sure:

https://www.elabpeers.com/arduino-data-logger.html


//Libraries
#include <DHT.h>
#include <U8glib.h>
#include <stdio.h>
#include <OneWire.h>
#include <Arduino.h>
#include <Adafruit_Sensor.h>
#include "DFRobot_EC.h"
#include <EEPROM.h>

#include <SD.h>
#include <Wire.h>
#include <RTClib.h>

// ADAFRUIT DATA LOGGER - A simple data logger for the Arduino analog pins
#define LOG_INTERVAL 1000 // mills between entries

//PINS
#define EC_PIN 1 //EC meter pin
#define PH_PIN 2 //pH meter pin
#define CO2_PIN 3 //ORP meter pin
#define DHT_PIN 5 // DHT pin
#define DHTTYPE DHT22 // DHT 22 (AM2302)
#define DS18B20_PIN 6 //EC solution temperature pin
#define CHIPSELECT_PIN 10 // for the data logging shield, we use digital pin 10 for the SD cs line
#define CHIPSELECT_PIN_LCD 4

DFRobot_EC ec;

// AVERAGING VALUES
#define MEDIAN_SAMPLE 8
#define MEASUREMENTS_TAKEN 100

// EC - solution temperature variables
#define StartConvert 0
#define ReadTemperature 1

// EC values - CHANGE THESE PARAMETERS FOR EC PROBE CALIBRATION
#define EC_PARAM_A 0.00754256

//pH values - CHANGE THESE PARAMETERS FOR PH PROBE CALIBRATION
#define PH_PARAM_A 1.0
#define PH_PARAM_B 0.0

#define XCOL_SET 55
#define XCOL_SET2 65
#define XCOL_SET_UNITS 85

//--------SENSOR GLOBALS--------
DHT dht(DHT_PIN, DHTTYPE);
U8GLIB_NHD_C12864 u8g(13, 11, CHIPSELECT_PIN_LCD, 9, 8);
int tmp, chk;
unsigned int AnalogAverage = 0, averageVoltage = 0, levelAverage;
float phValue, hum, temp, solution_temp, ECcurrent, co2;
OneWire ds(DS18B20_PIN);
//--------LOGGER GLOBALS--------
//RTC_DS1307 RTC; // define the Real Time Clock object
RTC_PCF8523 RTC;

char filename[13] = "LOGGER00.CSV";
unsigned long int last_record_time;
//------------------------------

void draw() {
  u8g.setFont(u8g_font_04b_03);
  
  u8g.drawStr( 0,11,"Temp:");
  u8g.setPrintPos(XCOL_SET,11);
  u8g.print(temp);
  u8g.drawStr( XCOL_SET_UNITS, 11,F("C"));
  
  u8g.drawStr(0,21,F("Humidity:"));
  u8g.setPrintPos(XCOL_SET,21);
  u8g.print(hum);
  u8g.drawStr( XCOL_SET_UNITS,21,F("%"));
  
  u8g.drawStr(0,31,F("pH:"));
  u8g.setPrintPos(XCOL_SET,31);
  u8g.print(phValue);
  
  u8g.drawStr(0,41,F("EC:"));
  u8g.setPrintPos(XCOL_SET,41);
  u8g.print(ECcurrent);
  u8g.drawStr( XCOL_SET_UNITS,41,F("mS/cm"));
  
  u8g.drawStr(0,51,F("Sol.Temp:"));
  u8g.setPrintPos(XCOL_SET,51);
  u8g.print(solution_temp);
  u8g.drawStr( XCOL_SET_UNITS,51,F("C"));
  
  u8g.drawStr(0,61,F("CO2:"));
  u8g.setPrintPos(XCOL_SET,61);
  u8g.print(co2);
  u8g.drawStr( XCOL_SET_UNITS,61,F("ppm"));
}

/*float TempProcess(bool ch) {
  static byte data[12];
  static byte addr[8];
  static float TemperatureSum;
  
  if(!ch){
    if ( !ds.search(addr)) {
      ds.reset_search();
      return 0;
    }
    if ( OneWire::crc8( addr, 7) != addr[7]) {
      return 0;
    }
    if ( addr[0] != 0x10 && addr[0] != 0x28) {
      return 0;
    }
    ds.reset();
    ds.select(addr);
    ds.write(0x44,1);
  }
  else{
    byte present = ds.reset();
    ds.select(addr);
    ds.write(0xBE);
    
    for (int i = 0; i < 9; i++) {
      data[i] = ds.read();
    }
    
    ds.reset_search();
    byte MSB = data[1];
    byte LSB = data[0];
    float tempRead = ((MSB << 8) | LSB);
    TemperatureSum = tempRead / 16;
  }
  
  return TemperatureSum;
}
*/

void calculateAnalogAverage(int pin) {
  int buf[MEASUREMENTS_TAKEN];
  unsigned long int avgValue = 0;
  AnalogAverage = 0;
  for(int i=0;i<MEASUREMENTS_TAKEN;i++) {
    buf[i]=analogRead(pin);
    delay(10);
  }
  for(int i=0;i<MEASUREMENTS_TAKEN-1;i++) {
    for(int j=i+1;j<MEASUREMENTS_TAKEN;j++) {
      if(buf[i]>buf[j]) {
        tmp=buf[i];
        buf[i]=buf[j];
        buf[j]=tmp;
      }
    }
  }
  for(int i=(MEASUREMENTS_TAKEN/2)-(MEDIAN_SAMPLE/2);i<(MEASUREMENTS_TAKEN/2)+(MEDIAN_SAMPLE/2);i++) {
    avgValue+=buf[i];
  }
  AnalogAverage = avgValue/MEDIAN_SAMPLE;
}

void read_pH() {
  calculateAnalogAverage(PH_PIN);
  phValue=(float)AnalogAverage*5.0/1024;
  phValue = 3.5*phValue+.52;
  //phValue=PH_PARAM_A*phValue+PH_PARAM_B;
}

void read_EC() {
  calculateAnalogAverage(EC_PIN);
  
 // solution_temp = TempProcess(ReadTemperature);
  //TempProcess(StartConvert);
  float temp1 = dht.readTemperature();
  averageVoltage=AnalogAverage*(float)5000/1024;
  //float TempCoefficient=1.0+0.0185*(solution_temp-25.0);
  //float CoefficientVolatge=(float)averageVoltage*TempCoefficient;
  ECcurrent = ec.readEC(averageVoltage,temp1);
  
  //ECcurrent=EC_PARAM_A*CoefficientVolatge;
}

void read_CO2() {
  float voltage;
  float voltage_difference;
  calculateAnalogAverage(CO2_PIN);
  voltage = AnalogAverage*(5000/1024.0);
  
  if(voltage == 0) {
    co2=-100.0;
  }
  else if(voltage < 400) {
    co2=0.0;
  }
  else {
    voltage_difference=voltage-400;
    co2=voltage_difference*50.0/16.0;
  }
}


void log_record(unsigned long int m) {
  File logfile;
  //DateTime record_time;
  Serial.println(filename);

 //   if (! rtc.initialized()) {
   // Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
  
  SdFile::dateTimeCallback(dateTime);
  logfile = SD.open(filename, FILE_WRITE);
  Serial.println(logfile);
  logfile.print(m); //log milliseconds since start
  logfile.print(", ");
  
  DateTime now = RTC.now(); // fetch the time
  //logfile.print(record_time.unixtime()); //log time - seconds since 1/1/1970
  //logfile.print(" ");
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);


  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.println(now.second(), DEC);
  
  logfile.print(",");
  logfile.print(temp);
  logfile.print(",");
  logfile.print(hum);
  logfile.print(",");
  logfile.print(phValue);
  logfile.print(",");
  logfile.print(ECcurrent);
  logfile.print(",");
  logfile.print(solution_temp);
  logfile.print(",");
  logfile.print(co2);
  logfile.print("\n");

  

  logfile.close();
}

void dateTime(uint16_t* date, uint16_t* time) {
  DateTime now = RTC.now();

  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(now.year(), now.month(), now.day());

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(now.hour(), now.minute(), now.second());
}

void setup() {
  //---------SENSOR SETUP---------
  pinMode(13,OUTPUT);
  Serial.begin(9600);
  
  dht.begin();
  u8g.setContrast(0);
  u8g.setRot180();
//  TempProcess(StartConvert);
  //------------------------------

  //---------LOGGER SETUP---------
  pinMode(10, OUTPUT); //make sure that the default chip select pin is set to output, even if you don't use it.
  //pinMode(4, OUTPUT);

  if (!SD.begin(CHIPSELECT_PIN)) { //see if the card is present and can be initialized:
    return; // don't do anything more
  }
  
  last_record_time = 0;
  File newfile;
  
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) { //only open a new file if it doesn't exist
      newfile = SD.open(filename, FILE_WRITE);
      break; // leave the loop!
    }
  }

  Wire.begin();
  if (!RTC.begin()) {
    newfile.println(F("RTC failed"));
  }
    //if (! rtc.initialized() || rtc.lostPower()) {
   // Serial.println("RTC is NOT initialized, let's set the time!");
  //RTC.adjust(DateTime(F(__DATE__), F(__TIME__))); 
  newfile.println(F("Millis,Time,Temp,Humidity,pH,EC,Solution Temp,CO2"));
  newfile.close();
  //------------------------------ 
}


void loop() {
    
  digitalWrite(13, HIGH);
  delay(800);
  digitalWrite(13, LOW);
  
  hum = dht.readHumidity();
  temp= dht.readTemperature();
  read_pH();
  read_EC();
  read_CO2();
Serial.print("1");
  unsigned long int time_since_start = millis();
  if (time_since_start > last_record_time  + LOG_INTERVAL) {
    log_record(time_since_start);
    last_record_time = time_since_start;
  }

Serial.print("2");
  u8g.firstPage();
  do {
    draw();
  }
  while( u8g.nextPage() );
Serial.print("3");
}

@ardytemp, please edit your post, select all code and click the </> button to apply so-called code tags and next save your post. It makes it easier to read, easier to copy and prevents the forum software from incorrect interpretation of the code.

I would start with the LCD only and see if that works with your modification (pin 4). May we assume that the LCD worked with the original setup (pin 10)?

I have two suggestions:

  1. Connect only one device at a time until you successfully get all your devices working. I do this on every project where I have devices I've never worked with before. So I have a bunch of small programs that "exercise" each device.

  2. For I2C devices you should load and run the I2C scanning program before you load your code. The I2C scanner will test to see if you I2C device is connected and responding. Not that it is working but just that it is responding.

The I2C scanner will jump up in multiple places in a google search. Here is one I2C Scanner

That data logging shield looks like a copy of the Adafruit design.

There is lots of information about it at:
https://learn.adafruit.com/adafruit-data-logger-shield?view=all#overview

This board has the facility to change the pin used for the SD card Chip Select pin from pin 10 to any other.

It might be better to change the datalogger to use pin 4 and leave pin 10 for the LCD shield. That way the LCD shield can be stacked on top of the data logger shield which should be more reliable than using the jumper wires.

You need to cut the track near pin 10 as indicated by the yellow arrow on the picture below, then solder a link between the CS pad and the pad for pin 4, which is very close by.

Ok I made the edits.

Yes the original pin works. I attached the lcd directly to the data logger, but modified the code so it didn't have of the data logging portions on in. The data displayed on the lcd appeared correct.

Are you suggesting I use jumper wires to connect the lcd to the uno (to use pin 4 instead of 10) but don't include the datalogging portions of the code?

Yes, that way you can determine if that part of the hatdware/code works or not. If not, remove the datalogger shield and try again.

Thank you for your help. Do you know what the rules are for posting this elsewhere on arduino forum?

Do you know of any other resources that can be used where I might get additional input?

Not sure if I understand. One rule would be that you should never post the same topic in two places on this forum; it's called cross posting and wastes peoples time because person A might give the same answer here that person B else already give in the other topic. Another one would be not to post in Installation and Troubleshooting if the problem relates to your project.

If you think that there is a better place on this forum, you can open the topic and click the black pencil next to the topic title. You will see a dropdown box where you can select the category. Select the one you want and next click the green button with the tick in it.

No idea.

Thanks again for your help. i tested the wiring as you suggested, but it worked as it was intended to (the LCD still worked when I connected pin 10 to pin 4).

However, I think I found the issue in the code. Can you help?

I think I found the portion of the code that is causing issues:

if (!SD.begin(chipSelect)) {
// don't do anything more:
return;
}

when i have these lines commented out, the LCD works well (the numbers look correct and the change with the environmental conditions). Unfortunately, the data logger does not record data. In fact, there aren't any files created on the SD card -- its empty.

However, when I include the above lines of code, the data logger works as intended, but the LCD has a bunch of noise making it impossible to read, and the displayed measurements do not update.

I can't figure out why these lines are so important--do you have any ideas why they are causing the LCD and data logger to misbehave?

If SD.begin() fails, you will have to sort that out.

  1. In a first step, try to run an example of the SD card library; adjust the CS pin if needed.
  2. If (1) doesn't work, strip the setup to only the Arduino and the datalogger shield and try again.
  3. If (2) doesn't work, I would suspect the datalogger shield or the SD card.

Let us hear how far you get.

The return statement causes the rest of the setup() to be skipped. The important part there is that Wire.begin() and RTC.begin() are not executed; any attempt to access something on the I2C bus on an non-initialised I2C bus or to access the RTC will result in rubbish or might invoke undefined behaviour.

I would probably modify setup() as shown below. First add the followingnear the top of your code

bool sdOK = true;
bool rtcOK = true;

Those are two flags that keep track of the status of the SD and the RTC. setup() can than look like

void setup() {
  //---------SENSOR SETUP---------
  pinMode(13, OUTPUT);
  Serial.begin(9600);

  dht.begin();
  u8g.setContrast(0);
  u8g.setRot180();
  //  TempProcess(StartConvert);
  //------------------------------

  //---------LOGGER SETUP---------
  pinMode(10, OUTPUT); //make sure that the default chip select pin is set to output, even if you don't use it.
  //pinMode(4, OUTPUT);

  if (!SD.begin(CHIPSELECT_PIN)) { //see if the card is present and can be initialized:
    sdOK = false;
  }

  last_record_time = 0;

  Wire.begin();
  if (!RTC.begin()) {
    rtcOK = false;
  }

  if (sdOK == true)
  {
    File newfile;

    for (uint8_t i = 0; i < 100; i++) {
      filename[6] = i / 10 + '0';
      filename[7] = i % 10 + '0';
      if (! SD.exists(filename)) { //only open a new file if it doesn't exist
        newfile = SD.open(filename, FILE_WRITE);
        break; // leave the loop!
      }
    }
    // if opening the file succeeded
    if (newfile)
    {
      if (rtcOK == false)
      {
        newfile.println(F("RTC failed"));
      }
      newfile.println(F("Millis,Time,Temp,Humidity,pH,EC,Solution Temp,CO2"));
      newfile.close();
    }
  }

  //if (! rtc.initialized() || rtc.lostPower()) {
  // Serial.println("RTC is NOT initialized, let's set the time!");
  //RTC.adjust(DateTime(F(__DATE__), F(__TIME__)));

  //------------------------------
}

It demonstrates a few ideas

  1. if (sdOK == true) will prevent the code from trying to access the SD card.
  2. you should always check if opening a file succeeded beforeattempting to read or write it; that;s the purpose of if (newfile).
  3. if (rtcOK == false) reports on the status.

In the rest of your code, you should apply the same ideas. In log_record()

void log_record(unsigned long int m) {
  if (sdOK == false)
  {
    Serial.println("SD error");
    return;
  }

  if (rtcOK == false)
  {
    Serial.println("RTC error");
    return;
  }

  File logfile;
  ...
  ...
  DateTime now = RTC.now(); // fetch the time

  if (logFile)
  {
    //logfile.print(record_time.unixtime()); //log time - seconds since 1/1/1970
    //logfile.print(" ");
    logfile.print(now.year(), DEC);
    ...
    ...
    logfile.close();
  }
}

There might be more places where you will have to implement the ideas.