I2C disrupts sketch

I have an arduino control my greenhouse in a simple way. The temperature (DHT11) as sensor to open and close windows via relay and an actuator. And buttons to override. Also a relay to control irrigation. The latter decided by the time after wake-up and a button. The duration controlled by a potentiometer. No delays, everything with millis.

It works fine, but I wanted to include a lcd to keep an eye on the parameters. I'm using an I2C one, but as soon I connect the SDA and SCL the whole system is up the spout. The buttons don't respond, but it seems also the rest doesn't work. I use a nano every in the greenhouse, but also tested it with a uno and other parts, to be sure it isn't an hardware problem. What am I missing?

For informed help, please read and follow the instructions in the "How to get the best out of this forum" post, linked at the head of every forum topic.

You miss to present a circuit diagram and your code.

1 Like

You do not need to know every .01 degree or humidity and time of day (the plants do not read). Use an analog ships clock for weather and LEDs on a 595 or two to show "too high, just right, too low."

On the other hand... make your LCD work first (hello world, count to ten, place characters cleanly, display only changing data), then add buttons (read any press at any time), then read the DHT (only read once every other second) ... then use most of the remaining mpu time reading buttons, then display only changed data precisely on the LCD.

Here the code. The lcd works fine, only after connecting the rest doesn't work anymore.

#include <dht.h>
dht DHT;
#define DHT11_PIN 5//TEMPERATUUR
#define btn1 9 //OPEN
#define btn2 11 //DICHT
#define btn3 10 //WATER

int pinLicht = A0;
int pinPotmeter = A1;
int pinOpen= 3;
int pinDicht= 4;
int pinWater=6;

// ------------------ ALLE INSTELBARE TIJDWAARDEN -------------------
unsigned long raamT=1000UL;//----------------- 6,8 SEC -----------
unsigned long tijdWater=300UL;//---------- 15 SEC -----------
unsigned long interval = 10*1000UL;//------ 10 MIN -----------
unsigned long meetInterval = 2000UL;//------ 2 MIN -----------
unsigned long lcdTijd = 2000UL;//-------------- 2 SEC --------------
int irrigatieUur=2;//-----------------------------  3DE UUR--------

int licht;
int lichtData[3]={10,10,10};// LAAG ZODAT GEMIDDELD LAAG, DUS BEGINT BIJ DAG(1043/3 < naarDag)
int q;//TELLER VOOR ARRAY
int n=0; // PERIODETELLER VOOR GEMIDDELD LICHT, BIJ BEGIN NACHT TERUG NAAR 0
int lichtGemiddeld;

int tempBinnen;
int tempOptel=0;
int tempGemiddeld=0;

int raamStand=0 ;
int inBeweging=0; // BEWEGING RAAM VASTZETTEN
int doelStand=0;
int raam=0;
int water=0;
int dagTeller=0;
int uurTeller;
int periodeTeller=0;

unsigned long nuMillis;
unsigned long nuRaam;
unsigned long eerderRaam;
unsigned long eerderMillis = 0UL;
unsigned long lcdMillis = 0UL;
unsigned long schermMillis = 0UL;
unsigned long startWater;

int k=1;//TELLER IN MEETPERIODE VOOR GEMIDDELDE TEMP

byte laatsteTijdKnop1 = LOW;
byte laatsteTijdKnop2 = LOW;
byte laatsteTijdKnop3 = LOW;

unsigned long knopPauze = 50UL; // millis
unsigned long vorigeKnop1Stand = 0UL;
unsigned long vorigeKnop2Stand = 0UL;
unsigned long vorigeKnop3Stand = 0UL;

int cyclus=11; // NA RESET ALTIJD BIJ "DAG" BEGINNEN (NACHT = 0)
int resetswitch=1;//VOOR NACHTRESET
int naarDag=400;//-----------
int naarNacht=980;//-----------

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address and the number of columns and rows

byte p;//PARAMETER POTMETER VOOR INSTELLEN DUUR IRRIGATIE
byte x=4;//FACTOR VOOR DUUR IRRIGATIE (X=20 == 5 MIN)
byte sec;
byte minW;
byte l=0;// TELLER VOOR SCHERMKOLOM

String TEM=(" graden C    ");
String WAT=(" min ");
String TAB=("  ");

//--------------SET UP ---------------------------
void setup(){

Serial.begin(9600);
lcd.begin();

pinMode(pinLicht, INPUT);
pinMode(pinPotmeter, INPUT);

pinMode(btn1, INPUT_PULLUP);
pinMode(btn2, INPUT_PULLUP);
pinMode(btn3, INPUT_PULLUP);

pinMode(pinOpen, OUTPUT);
pinMode(pinDicht, OUTPUT);
pinMode(pinWater, OUTPUT);

digitalWrite(pinWater, HIGH);
digitalWrite(pinOpen, HIGH);
digitalWrite(pinDicht, HIGH);
}

//----------------------- LOOP -----------------------------------------
void loop(){

licht = analogRead(pinLicht);

switch(cyclus){// DAG/NACHT(11/0)

//-------NACHT--------------------------------------
  case 0:
  
  nuMillis = millis();

//------INTERVAL NACHT (10 MIN)--------------------------------------------
  if(nuMillis - eerderMillis > interval) {
    if (resetswitch==1) {
      resetswitch=0;
      periodeTeller=0;
      ++dagTeller;
      n=0;
    } // EINDE RESET LOOP

  eerderMillis = millis();

 //-------GEMIDDELDE LICHTSTERKTE NACHT--------------------------------------
    q=n%3;
    lichtData[q]=licht;
    lichtGemiddeld = (lichtData[0] + lichtData[1] + lichtData[2])/3; // SIMPEL EN ZONDER LOOP 
    ++n;
    
    if (lichtGemiddeld < naarDag) {
      cyclus=11;
    }
    else {cyclus=0;}    
        
  } // EINDE NACHT-INTERVAL LOOP

  break;

//-------DAG--------------------------------------
    case 11:

  resetswitch=1; 
  nuMillis = millis();

//-------GEMIDDELDE TEMP DAG, POTMETER, irrigatieUur ------------------
    if(nuMillis - eerderMillis > k*meetInterval) {

      int chk = DHT.read11(DHT11_PIN);
      tempBinnen=DHT.temperature;
      
      tempOptel += tempBinnen;
      tempGemiddeld = tempOptel/k;
      ++k;

      p = analogRead(pinPotmeter);
      x=map(p,0,255,2,20);//VERDEELD OVER SCOPE POTMETER; VAN 1 T/M 5 MIN     
      }
    
// ------AANZETTEN WATER ------------------------------------
if (uurTeller==irrigatieUur) {
    water=1;
}

startWater=interval-x*tijdWater;

if (water==1) {
  if (nuMillis - eerderMillis > startWater) {
    digitalWrite(pinWater,LOW); 
  } 
}

//------INTERVAL (10 MIN)--------------------------------------------
  if(nuMillis - eerderMillis > interval) {
        
    raamRichting(tempGemiddeld, raamStand);  
         
    ++periodeTeller;
    uurTeller=periodeTeller/6;
    k=1;
    tempOptel=0;

//-------------------LICHT DAG ------------------------------------------
    q=n%3;
    ++n;
    lichtData[q]=licht;
    lichtGemiddeld = (lichtData[0] + lichtData[1] + lichtData[2])/3; // SIMPEL EN ZONDER LOOP 
    
    if (lichtGemiddeld > naarNacht) {
      cyclus=0;
    }
    else {cyclus=11;}

      eerderMillis = millis();
    
// --------- UITZETTEN WATER ------------------------------------------
    digitalWrite(pinWater,HIGH);
    water=0;

  }//EINDE INTERVAL LOOP

//-------RAAM OPEN/DICHT----------------------------------------------
switch (raam) {//BEGIN RAAM SWITCH LOOP

      case 0:
      
      break;

      case 1:

      nuRaam=millis();

      if(inBeweging==0) {
        digitalWrite(pinOpen, LOW);
        inBeweging=1;
        eerderRaam=millis();
      }
      else if(nuRaam - eerderRaam > raamT) {       
          digitalWrite(pinOpen, HIGH);
          ++raamStand;
          inBeweging=0;
          raam=0;
      }
      
      break;

      case -1:

      nuRaam=millis();
      if(inBeweging==0) {
        digitalWrite(pinDicht, LOW);
        inBeweging=1;
        eerderRaam=millis();
      }

      else if(nuRaam-eerderRaam > raamT) {
          digitalWrite(pinDicht, HIGH);
          --raamStand;
          inBeweging=0;
          raam=0;
      }
      
      break;

    }//EIND RAAM SWITCH LOOP


// ------------------KNOPPEN -----------------------------

  if (knopOpen()) {
    raam=1;
    raamStand = raamStand - 1;

  }
  if (knopDicht()) {
    raam=-1;
    raamStand = raamStand + 1;

  }
  if (knopWater()) {
    water=1;
  }
  
}//EINDE DAG/NACHT SWITCH LOOP

// ---------- TEMP/WATER OP LCD -----------------------------
  sec=15*x%60;
  minW=x/4;
  String Info1=(tempGemiddeld + TEM + raamStand);
  String Info2=(minW + WAT + sec + TAB + lichtGemiddeld + TAB);

if (nuMillis - lcdMillis > 10 && n<17) {
    lcd.setCursor(n, 0);
    lcd.print(Info1.charAt(n));
    lcd.setCursor(n, 1);
    lcd.print(Info2.charAt(n));
    lcdMillis=nuMillis;
    ++n;
}

if (nuMillis-schermMillis>lcdTijd) {
  lcd.clear();
  schermMillis=nuMillis;
  n=0;

}// EINDE SCHERMVAST


}// -- EINDE LOOP --

//--------- FUNCTIE RAAMRICHTING --------------------------
int raamRichting(int gemiddeldeTemp, int raamStand) {

      if(gemiddeldeTemp<20) {
        doelStand=0;
      }
      else if (gemiddeldeTemp>34) {
        doelStand=5;
        }
      else {doelStand=map(gemiddeldeTemp, 20, 34, 1, 5);}
      
      if(doelStand<raamStand) {
        raam=-1;
      }
      else if(doelStand>raamStand) {
        raam=1;
      }
      else{
        raam=0;
      }
      
      return raam;
      

  }//EINDE CODE FUNCTIE RAAMRICHTING

// ------KNOPPEN OPEN/DICHT/WATER ----------------------------  

bool knopOpen() {
  static uint16_t state = 0;
  state = (state<<1) | digitalRead(btn1) | 0xfe00;
  return (state == 0xff00);
}
bool knopDicht() {
  static uint16_t state = 0;
  state = (state<<1) | digitalRead(btn2) | 0xfe00;
  return (state == 0xff00);
}
bool knopWater() {
  static uint16_t state = 0;
  state = (state<<1) | digitalRead(btn3) | 0xfe00;
  return (state == 0xff00);
}

The String type can not work well on an Uno due to memory restrictions. Dunno how much RAM is in your other controllers.

What value if pull resistors did you use on ACL and SDA? What did the I2C scanner show?

Could be a wiring problem.

Please post a wiring diagram. Hand drawn is preferred, with parts and connections clearly labeled.

I think you might have an old sketch with a new library. Verify in your library if this is correct, or maybe lcd.init(); After that change, it seemed to work.

for WOKWI.COM

diagram.json
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot1", "top": -1.3, "left": 230.2, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot2", "top": 113.9, "left": 230.2, "attrs": {} },
    {
      "type": "wokwi-pushbutton-6mm",
      "id": "btn4",
      "top": -31,
      "left": 163.2,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton-6mm",
      "id": "btn1",
      "top": -50.2,
      "left": 163.2,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton-6mm",
      "id": "btn2",
      "top": -69.4,
      "left": 163.2,
      "attrs": { "color": "green" }
    },
    { "type": "wokwi-led", "id": "led1", "top": -80.4, "left": -25, "attrs": { "color": "red" } },
    { "type": "wokwi-led", "id": "led2", "top": -118.8, "left": -25, "attrs": { "color": "red" } },
    { "type": "wokwi-led", "id": "led3", "top": -157.2, "left": -25, "attrs": { "color": "red" } },
    {
      "type": "wokwi-lcd1602",
      "id": "lcd1",
      "top": -147.2,
      "left": 236,
      "attrs": { "pins": "i2c" }
    }
  ],
  "connections": [
    [ "pot1:GND", "nano:GND.1", "black", [ "v19.2", "h-115.2" ] ],
    [ "pot1:SIG", "nano:A0", "green", [ "v28.8", "h-192.4" ] ],
    [ "pot1:VCC", "nano:5V", "red", [ "v38.4", "h-125.6" ] ],
    [ "pot2:GND", "nano:GND.1", "black", [ "v19.2", "h-86.4" ] ],
    [ "pot2:SIG", "nano:A1", "green", [ "v28.8", "h-134.8" ] ],
    [ "pot2:VCC", "nano:5V", "red", [ "v38.4", "h-116" ] ],
    [ "nano:GND.2", "btn4:2.l", "black", [ "v0" ] ],
    [ "nano:9", "btn4:1.l", "green", [ "v0" ] ],
    [ "nano:GND.2", "btn1:2.l", "black", [ "v0" ] ],
    [ "nano:GND.2", "btn2:2.l", "black", [ "v0" ] ],
    [ "nano:10", "btn1:1.l", "green", [ "v0" ] ],
    [ "nano:11", "btn2:1.l", "green", [ "v0" ] ],
    [ "nano:GND.2", "led1:C", "black", [ "v-14.4", "h-144.5", "v-28.8" ] ],
    [ "nano:GND.2", "led2:C", "black", [ "v-14.4", "h-144.5", "v-67.2" ] ],
    [ "nano:GND.2", "led3:C", "black", [ "v-14.4", "h-144.5", "v-105.6" ] ],
    [ "led1:A", "nano:3", "green", [ "v0", "h134.4" ] ],
    [ "led2:A", "nano:4", "green", [ "v0", "h105.6" ] ],
    [ "led3:A", "nano:6", "green", [ "v0", "h105.6" ] ],
    [ "lcd1:SCL", "nano:A5", "green", [ "h-19.2", "v201.9", "h-134.4" ] ],
    [ "lcd1:SDA", "nano:A4", "green", [ "h-28.8", "v221", "h-134.4" ] ],
    [ "lcd1:VCC", "nano:5V", "red", [ "h-38.4", "v240.1", "h-76.8" ] ],
    [ "lcd1:GND", "nano:GND.2", "black", [ "h0" ] ]
  ],
  "dependencies": {}
}
1 Like

If you power LCD from 5V pin of Arduino, the power may be not enough. Please try to use external power source for LCD. please do not forget to connect GND of the extra power source to the GND of Arduino. You can refer to the below article to see how to use the external power source: https://arduinogetstarted.com/faq/how-to-use-external-power-supply-for-arduino

Thanks for all your comments. Great to have some help here, since I'm (still) lost.

@IoT I power uno, sensors, relays and lcd unit from external source.

@xfpd The lcd prints out the values I want, perfectly. Only once connected the program doesn't seem to work. No response to buttons and no response from relays to temp and time.

@jremington Since all else works perfectly, when SDA/SCL are not connected, I presume wiring is fine.

@gilshuttz I use no resistors for those ports. Didn't know you had to. What scan can I do and why?

@Dr Dietrich I tried taking most of the strings out and even only just printing one parameter. Still, as soon as I connect SDA/SCL, sketch (at least, the parts that matter for controlling the greenhouse) seems to go haywire.

Did the scan:
Scanning...

I2C device found at address 0x27 !

done

Read the temp and time, then display the data at two-seconds interval. All the time between should be spent reading buttons.

OK, thanks, is a possibility. I will try it. But still wonder why attaching LCD has such impact.

In other words, wiring.

1 Like

@jremington
The buttons are wired from the ports indicated in the sketch to earth. The photoresistor and DHT11 also connected to the right ports. Nothing to A4/A5, so as not to interfere with SDA/SCL. The LCD connected the right way to SDA/SCL. And independently (LCD part and relay part) everything works fine. You're absolutely right, it has something to do with wiring both parts. What I'm interested in is what and why, so please enlighten me.

See post #2, #3, #8, and post a photo of the setup.

Check all solder connections for solder bridges or poor connections.

One thing you may not be aware of is that the i2c implementation on the AVR platform is performed half by the h/w and half by the s/w.
There are some issues in the AVR Wire library that can cause the AVR processor hang in a s/w spin loop.
This is because under certain conditions (like glitches on the i2c signals) the AVR Wire library s/w gets confused and can enter a state where it is waiting for an event that is never going to happen and the library s/w doesn't ever timeout. It will just spin forever waiting/looking for the event.
The newer AVR Wire library has some configurable options to enable timeouts to keep the processor from hanging for most of these scenarios.
When a timeout occurs the Wire library will return an error status.
But I don't think these new timeouts are enabled by default.
i.e. the sketch has to call an AVR Wire library specific function to enable them.
This API call is not portable across other Wire library implementations and there is no way to tell if it exists, in the Wire library you may be using.
This makes it very unfriendly to use when writing portable code.

That said, most code (library or sketch code) that uses the Wire library does not check the return status when writing to i2c slaves so even if the timeouts were to be enabled either by the sketch or by default, the timeouts keeping the AVR from hanging may have limited value since it just moves the problem to somewhere else.

This is because even if s/w that uses the Wire library did check return codes for errors & exceptions, recovering from this is not always simple.

For example for this LCD h/w (a PCF8574 used to control a hd44780 LCD in 4 bit mode), a timeout (which means some data being written to the PCF8574 was lost) can cause the host (the Arduino board) and the LCD to get hopelessly out of nibble sync. When this happens the LCD will start displaying garbage since it is misinterpreting instructions by creating the 8 bit instruction half from one byte and half from the next byte.
The only way to get back in sync is either a power cycle or to re-initialize the LCD.
This is long way of saying that for this type of LCD device, a simple retry when there is an error returned from the Wire library is unlikely to work as a full LCD initialization is likely needed to get the host (Arduino board) and LCD back into nibble sync with each other before the host can send any additional instructions to the LCD.
And if a re-initalization is done, then all the previous settings / modes / and characters on the display are lost.

============================================

I would suggest that you need to figure out if you are falling to this AVR processor hang situation down in the Wire library or if the sketch is still running but simply not working as intended.
Maybe blink the onboard LED in your loop() so if that stops, the Processor is hung.

Things that can cause the Wire library hang issue are typically signal integrity issues which can be caused by things like poor solder connections, poor power supply (noise),
improper pullup resistors on the signals, or poor wiring like shorted wires, or wires that are too long or wires that are picking noise from other signals.

You could try installing the hd44780 library,
it has support for this type of LCD display device.
It also includes a diagnostic test that will test the i2c lines for shorts, test for pullup resistors and then test the LCD internal memory.

The hd44780 library is available through the IDE library manager.
It has multiple i/o classes for different displays. The i/o class for your hardware
is hd44780_I2Cexp.
The diagnostic sketch is called I2CexpDiag.

Porting your sketch to use the hd44780_I2Cexp i/o class is easy as you only have to
change the include files and the lcd object declaration.

Here is a link to the library wiki:
https://github.com/duinoWitchery/hd44780/wiki

And a direct link to the wiki for the hd44780_I2Cexp i/o class:
https://github.com/duinoWitchery/hd44780/wiki/ioClass:-hd44780_I2Cexp

--- bill

1 Like