Code Stalling on Display.Display

Hey Everyone,

I'm running into a bit of a head scratcher. I am making a project that uses moisture sensors to see if a plant needs more water, and then uses pumps to water them if needed.

I am running into a weird problem.
the program does the following when it see's the soil needs water

  1. turns on pump
  2. call's function makeRain (which updates OLED screen to raindrops, and plays animation for the needed time)
  3. turns off pump
  4. waits 2.5 seconds before checking next flower pot)
  if(moisturePercent < pumpOn){
        digitalWrite(outputPin, HIGH);
        makeRain(waterpumpDelay);
        digitalWrite(outputPin, LOW);
        delay(2500);
        return true;

But for what ever reason, it keeps stalling on the makeRain function:

void makeRain(int delayTime, int width = 128, int height = 62){
  long StartTime = millis();
  int rwidth  = 10;
  int rheight = 15;
  int totalLines = height / rheight;
  int line = 1;
  int ind;
  while((delayTime*1000) > (millis() - StartTime)){
   display.clearDisplay();
   for(int ii = 0; ii<totalLines; ii+=1){
    ind = 0;
    if(line % 2 == 0){ind = (rwidth/2);}
    line+=1;
     for(int i = 0; i<width; i+= (rwidth+3)){
        display.drawBitmap(i+ind, (ii*rheight), rainDrop, rwidth, rheight, WHITE);
     }
   }
   line+=1;
//   display.display();
   delay(1000);
  }
}

I have found two ways to stop it from stalling, both of them remove weird, and don't work towards the final project

  1. Remove the "display.display()" in the make rain function... which removes the animation, thus nullifying the whole function

  2. unplug the motors from the circuit, which prevents the plants from getting water, which is the point of the whole project

I also tried using a strong psu. it was a 5v 5a. Should have been more than suitable.

I am thinking it's either a problem with the display Lib,

or maybe it's too processor heavy, and it's crash.

What do you guys think?

Here is the project details:

1x Arduino Uno
4 x Submergible Pumps
1 x Uln2308a
1 x 5v 2a power supply
4 x moisture sensors
1 x 128x32px oled display

Please post you full code.
You should remove the delays, but that takes a bit or work
i.e. remove the while(delayTime...
See my tutorials How to Write Timers and Delays in Arduino
and Multi-tasking in Arduino

If anyone needs to see the code, here it is:

Here is the code:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display(-1);
const unsigned char rainDrop[] PROGMEM = {0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x3f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0xff, 0xc0, 0xbf, 0xc0, 0xbf, 0xc0, 0xdf, 0xc0, 0xdf, 0x80, 0x7f, 0x80, 0x1f, 0x00};

void makeRain(int delayTime, int width = 128, int height = 62){
  long StartTime = millis();
  int rwidth  = 10;
  int rheight = 15;
  int totalLines = height / rheight;
  int line = 1;
  int ind;
  while((delayTime*1000) > (millis() - StartTime)){
   display.clearDisplay();
   for(int ii = 0; ii<totalLines; ii+=1){
    ind = 0;
    if(line % 2 == 0){ind = (rwidth/2);}
    line+=1;
     for(int i = 0; i<width; i+= (rwidth+3)){
        display.drawBitmap(i+ind, (ii*rheight), rainDrop, rwidth, rheight, WHITE);
     }
   }
   line+=1;
   display.display();
   delay(1000);
  }
}

void splashScreen(){
  display.clearDisplay();
  display.setCursor(0,0);
//  display.drawBitmap(0, 0, titleScreen, 128, 32, WHITE); #removed this for post
  display.display();
}


void homeScreen(int p1, int p2, int p3, int p4){
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(0,0);
  display.println("Soil Stats");
  display.setTextSize(1);
  display.setCursor(0,17);
  display.println("Pot1:"+String(p1)+"%");
  display.setCursor(60,17);
  display.println("Pot2:"+String(p2)+"%");
  display.setCursor(0,25);
  display.println("Pot3:"+String(p3)+"%");
  display.setCursor(60,25);
  display.println("Pot4:"+String(p4)+"%");
  display.display();
}

class moistureSensor{
  private:
          int outputPin;

  public:             // Access specifier
          float pumpOn            = 21;
          float pumpOff           = 40;
          int airValue            = 426;
          int waterValue          = 242;
          double waterpumpDelay   = 3;
          float moistureValue;
          float moisturePercent;
          int input;
    
    moistureSensor(int input, int outputPin, double waterpumpDelay){
      this->input = input;
      setOutputPin(outputPin);
      this->waterpumpDelay = waterpumpDelay;
    }

    moistureSensor(int input, int outputPin, double waterpumpDelay,  int airValue, int waterValue, float pumpOff, float pumpOn){
      this->input = input;
      setOutputPin(outputPin);
      this->waterpumpDelay = waterpumpDelay;
      this->airValue = airValue;
      this->waterValue = waterValue;
      this->pumpOff = pumpOff;
      this->pumpOn = pumpOn;
    }

    void setOutputPin(int p){
      this->outputPin = p;
      pinMode(p, OUTPUT);
      digitalWrite(p, LOW);
    };

    void getSoilMoisture(){
      moistureValue = analogRead(input);
      moisturePercent = map(moistureValue, airValue, waterValue, 0, 100);
      if(moisturePercent >= 100){
        moisturePercent = 100;
      }
      if(moisturePercent <=0){
        moisturePercent = 0;
      }
      this->moistureValue = moistureValue;
      this->moisturePercent = moisturePercent;
    };

    bool updateOutput(){
      if(outputPin == 0){
        return false;
      }
    getSoilMoisture();
      if(moisturePercent < pumpOn){
        digitalWrite(outputPin, HIGH);
        makeRain(waterpumpDelay);
        digitalWrite(outputPin, LOW);
        delay(2500);
        return true;
      }
      if(moisturePercent > pumpOff){
        digitalWrite(outputPin, LOW);
        delay(2500);
        return true;
      }
      return false;
    };
};
moistureSensor ms1(A0, 7, 3, 445, 242, 21, 40);
moistureSensor ms2(A1, 6, 3, 431, 222, 21, 40);
moistureSensor ms3(A2, 5, 3, 426, 234, 21, 40);
moistureSensor ms4(A3, 4, 3, 466, 210, 21, 40);

void setup(){
  Serial.begin(9600);
    // initialize with the I2C addr 0x3C
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  splashScreen();
  // Clear the buffer.
  delay(2500);
  display.clearDisplay();
  display.display();

  // Display Text
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.display();

}

void loop() {
    Serial.println("A1 " + String(ms1.moisturePercent));
  ms1.updateOutput();
    Serial.println("A2 " + String(ms2.moisturePercent));
  ms2.updateOutput();
    Serial.println("A3 "+ String(ms3.moisturePercent));
  ms3.updateOutput();
    Serial.println("A4 "+ String(ms4.moisturePercent));
  ms4.updateOutput();

  homeScreen(ms1.moisturePercent, ms2.moisturePercent, ms3.moisturePercent, ms4.moisturePercent);
  delay(600);
}

drmpf:
Please post you full code.
You should remove the delays, but that takes a bit or work
i.e. remove the while(delayTime...
See my tutorials How to Write Timers and Delays in Arduino
and Multi-tasking in Arduino

I am not really sure why delay is not valid here. I don't really want the Arduino moving forward with code until the time has passed anyway. your article is suggesting that waiting for a delay to finish is a problem
My issue isn't with the delay, is its with "display.display()" which happens before the display. Even if I remove the delay all together, it still stall on that line
Is there something I am not understanding?

I was concerned with your

 while((delayTime*1000) > (millis() - StartTime)){
   display.clearDisplay();
   for(int ii = 0; ii<totalLines; ii+=1){
    ind = 0;
    if(line % 2 == 0){ind = (rwidth/2);}
    line+=1;
     for(int i = 0; i<width; i+= (rwidth+3)){
        display.drawBitmap(i+ind, (ii*rheight), rainDrop, rwidth, rheight, WHITE);
     }
   }
   line+=1;
   display.display();
   delay(1000);
  }

however from you full code it looks like you only do this for 3 sec. Still not good but...
Also you are using Strings again not good and not actually needed just use multiple prints()
e.g.

  Serial.print("A1 "); Serial.println(ms1.moisturePercent);

or if you like using Strings try my SafeString library replacement

So to debugging.
If you think the problem is the display just run the loop with only it running

void loop() {
  makeRain(3);
  }

if that works
add in some more code like

void loop() {
   ms1.getSoilMoisture();
  Serial.print("A1 ); Serial.println(ms1.moisturePercent);
  makeRain(3);
   ms2.getSoilMoisture();
  Serial.print("A2 ); Serial.println(ms1.moisturePercent);
  makeRain(3);
   ms3.getSoilMoisture();
  Serial.print("A3 ); Serial.println(ms1.moisturePercent);
  makeRain(3);
   ms4.getSoilMoisture();
  Serial.print("A4 ); Serial.println(ms1.moisturePercent);
  makeRain(3);
  }

Then add a bit more code and see which bit breaks

All the Serial.println() codes are being deleted. It was a rough way to find where the code was stalling.

I have already found where the code breaks. It's "display.display()" right before "delay(1000)" in the makeRain function.

It might be a memory issues, based on this post

The program is drawing the same bitmap over and over again to the buffer of the oled, I think it is creating a problem.

I am trying to see if turning the bit map into a custom character, and then placing it as a repeated string would be less memory intensive.... I just have to learn to do that.

Why would I want to reprogram it without delay? I want it to use one pump at time. I would have to program the loop ton constantly check if a pump is running, and wait... seems more logical to use a function that simply halts the loop where it's needed

Why would I want to reprogram it without delay?

Well in your particular project looks like it would be fine with delays, but in general they quickly cause problems.

I had a quick look at the display library code and did not see a problem
It allocates a buffer for the display, but only does it once.

(uint8_t *)malloc(WIDTH * ((HEIGHT + 7) / 8))))

Can you get any thing displayed at all?
Check the return from

display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

in setup. Does it return true?
i.e..

if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
   Serial.println(" display did not start!!");
}

The program worked perfect until that exact line. It displayed everything it should have. It would just stop at that display.display();.
For whatever reason, it only stalled if display.display() was called, while the pump was running.

I ended up "fixing" the problem by reworking the code so they wouldn't run at the same time.

#include <Wire.h>
#include <Adafruit_SSD1306.h>

//#include <Adafruit_GFX.h>
Adafruit_SSD1306 display(-1);
const unsigned char rainDrop[] PROGMEM = {0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x3f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0xff, 0xc0, 0xbf, 0xc0, 0xbf, 0xc0, 0xdf, 0xc0, 0xdf, 0x80, 0x7f, 0x80, 0x1f, 0x00};

void makeRain(int delayTime, int width = 128, int height = 62){
  long StartTime = millis();
  int rwidth  = 10;
  int rheight = 15;
  int totalLines = height / rheight;
  int line = 1;
  int ind;
  while((delayTime*1000) > (millis() - StartTime)){
   display.clearDisplay();
   for(int ii = 0; ii<totalLines; ii+=1){
    ind = 0;
    if(line % 2 == 0){ind = (rwidth/2);}
    line+=1;
     for(int i = 0; i<width; i+= (rwidth+3)){
        display.drawBitmap(i+ind, (ii*rheight), rainDrop, rwidth, rheight, WHITE);
     }
   }
   line+=1;
   display.display();
  }
}

void splashScreen(){
  display.clearDisplay();
  display.setCursor(0,0);
//  display.drawBitmap(0, 0, titleScreen, 128, 32, WHITE); #I took this out for the post
  display.display();
}


void homeScreen(int p1, int p2, int p3, int p4){

    if(p1 < 0 || p2 < 0 || p3 < 0 || p4 < 0){
      makeRain(2.5);
    }

    display.clearDisplay();
    display.setTextSize(2);
    display.setCursor(0,0);
    display.println("Soil Stats");
    display.setTextSize(1);
    
    
    display.setCursor(0,17);
    display.print(F("Pot1:"));
    
    display.setCursor(60,17);
    display.print(F("Pot2:"));
    
    display.setCursor(0,25);
    display.print(F("Pot3:"));
    
    display.setCursor(60,25);
    display.print(F("Pot4:"));

  if(p1>=0){
    display.setCursor(30,17);
    display.print(p1);
    display.print(F("%"));
  }else{
    display.setCursor(30,17);
    display.print(F("H2O"));
  }
  if(p2>=0){
    display.setCursor(90,17);
    display.print(String(p2));
    display.print(F("%"));
  }else{
    display.setCursor(90,17);
    display.print(F("H20"));
  }
  if(p3>=0){
    display.setCursor(30,25);
    display.print(p3);
    display.print(F("%"));
  }else{
    display.setCursor(30,25);
    display.print(F("H20"));  }
  if(p4>=0){
    display.setCursor(90,25);
    display.print(p4);
    display.print(F("%"));
  }else{
    display.setCursor(90,25);
    display.print(F("H20"));
  }
  display.display();
}

class moistureSensor{
  private:
          int outputPin;

  public:             // Access specifier
          float pumpOn            = 21;
          float pumpOff           = 40;
          int airValue            = 426;
          int waterValue          = 242;
          double waterpumpDelay   = 3;
          float moistureValue;
          float moisturePercent   = -1;
          int input;
    
    moistureSensor(int input, int outputPin, double waterpumpDelay){
      this->input = input;
      setOutputPin(outputPin);
      this->waterpumpDelay = waterpumpDelay;
    }

    moistureSensor(int input, int outputPin, double waterpumpDelay,  int airValue, int waterValue, float pumpOff, float pumpOn){
      this->input = input;
      setOutputPin(outputPin);
      this->waterpumpDelay = waterpumpDelay;
      this->airValue = airValue;
      this->waterValue = waterValue;
      this->pumpOff = pumpOff;
      this->pumpOn = pumpOn;
    }

    void setOutputPin(int p){
      this->outputPin = p;
      pinMode(p, OUTPUT);
      digitalWrite(p, LOW);
    };

    void getSoilMoisture(){
      moistureValue = analogRead(input);
      moisturePercent = map(moistureValue, airValue, waterValue, 0, 100);
      if(moisturePercent >= 100){
        moisturePercent = 100;
      }
      if(moisturePercent <=0){
        moisturePercent = 0;
      }
      this->moistureValue = moistureValue;
      this->moisturePercent = moisturePercent;
    };

    bool updateOutput(){
      if(outputPin == 0){
        return false;
      }
    getSoilMoisture();
      if(moisturePercent < pumpOn){
        delay(2500);
        digitalWrite(outputPin, HIGH);
//        makeRain(waterpumpDelay);
        delay(waterpumpDelay*1000);
        digitalWrite(outputPin, LOW);
        return true;
      }
      if(moisturePercent > pumpOff){
        digitalWrite(outputPin, LOW);
        delay(2500);
        return true;
      }
      return false;
    };
};

moistureSensor ms1(A0, 7, 3, 445, 242, 21, 40);
moistureSensor ms2(A1, 6, 3, 431, 222, 21, 40);
moistureSensor ms3(A2, 5, 3, 426, 234, 21, 40);
moistureSensor ms4(A3, 4, 3, 466, 210, 21, 40);

void setup(){
  Serial.begin(9600);
    // initialize with the I2C addr 0x3C
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  splashScreen();
  // Clear the buffer.
  delay(2500);
  display.clearDisplay();
  display.display();

  // Display Text
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.display();

}

void loop() {
  homeScreen(-1, ms2.moisturePercent, ms3.moisturePercent, ms4.moisturePercent);
  ms1.updateOutput();

  homeScreen(ms1.moisturePercent,-1, ms3.moisturePercent, ms4.moisturePercent);
  ms2.updateOutput();

  homeScreen(ms1.moisturePercent, ms2.moisturePercent, -1, ms4.moisturePercent);
  ms3.updateOutput();

  homeScreen(ms1.moisturePercent, ms2.moisturePercent, ms3.moisturePercent, -1);
  ms4.updateOutput();

  homeScreen(ms1.moisturePercent, ms2.moisturePercent, ms3.moisturePercent, ms4.moisturePercent);

  delay(60000);
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.