Program crashes during delay (I2C with RaspberryPi)

Hey guys,
I’m in dire need for a little help.

I’ve got an automatic fish-feeder, the motor is turned on with a relais. The relais is switched on and off from the Arduino. Also the arduino is connected to a Raspberry Pi via I2C to receive the timing for feeding, send measurements and receive codes for food amounts.
The Raspberrypi is storing the measurements in a database, providing a web-based user-interface and so on.

The whole thing was running for a few days, feeding a few times a day for multiple seconds.Then the motor in the fish-feeder suddenly didn’t stop running anymore. I wasn’t there so i don’t know then exactly. I’m pretty sure the code got stuck in feedcycle(), before turning the relais off again (=pulling feedPin to LOW):

digitalWrite(feedPin,HIGH);
delay((foodAmounts[i] * weightfactor) / feedcycles);
digitalWrite(feedPin,LOW);

Would you have any idea how thats possible? Whats the pin state when the Arduino crashes?

This is the whole (simplified) code:

#include <DHT.h>
#include <Wire.h>
#include <OneWire.h>
#include <DS18B20.h>
#include <SoftwareSerial.h>
#include <Servo.h>


#define DHTPIN 12
#define ONE_WIRE_BUS 4
SoftwareSerial phReader(2, 3);
#define hallPin A0
#define feedPin 5
#define servoPin 11
#define servoPowerPin 10


Servo feedServo;

#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

#define adresse 0x05

OneWire oneWire(ONE_WIRE_BUS);
DS18B20 waterTempSensor(&oneWire);

bool feedflag = false;
int code = 0;
int last_code = 0;
byte messwerte[] = {0,0,0,0,0,0,0,0,0,0,0};

int foodAmounts[] = {0,0,0,0,0};
int feedcycles = 1;
int servoPos[] = {0,70,0,115,180};
int weightfactor = 1748; //


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Wire.begin(adresse);
  Wire.onReceive(empfangeCode);
  Wire.onRequest(sendeMesswerte);

  waterTempSensor.begin();
  dht.begin();
  phReader.begin(9600);
  
  feedServo.attach(servoPin);
  feedServo.write(90);
  pinMode(servoPowerPin, OUTPUT);
  digitalWrite(servoPowerPin, HIGH);
  
  pinMode(feedPin, OUTPUT);
  digitalWrite(feedPin, LOW);


}


void loop() {
    if (feedflag==true){
      feedcycle();
      feedflag = false;
    }

}


void empfangeCode(int byteCount) {
  while (Wire.available()) {
    code = Wire.read();
  }
   // doing stuff based on the [i]code[/i] (e.g. setting the foodamount)
   if (code == 1) {
         feedflag = true;
   }
}


void sendeMesswerte() {
  //sending measurements with Wire.write()
}



void dht11() {
    // selfexplanatory
}



void digitalWaterSensor() {
    // selfexplanatory    
}



void phController() {
  // connected to a pH-Controller via Software Serial
}


void feedcycle() {

  for (int i = 0 ; i<=4; i++){
    if (foodAmounts[i] != 0){
      digitalWrite(servoPowerPin, HIGH); 
      feedServo.write(servoPos[i]);
      delay(1000);
      digitalWrite(servoPowerPin, LOW);
      digitalWrite(feedPin,HIGH);
      delay((foodAmounts[i] * weightfactor) / feedcycles);
      digitalWrite(feedPin,LOW);
      // 
      delay(1000);
    }
  }

}

I was lucky there wasn’t any food in the feeder, otherwise the whole thing would have emptied into the pond.

Thanks a lot in advance!

I can't see anything obvious with the code you posted; however, since that code is not complete it may be in a portion of the code you removed. I think we will need to entire sketch to figure this out.

Post your code. All of it. If it's too long, you can attach it.

What is feedcycles for? I'm suspicious that it may be causing a divide by zero, although I didn't notice anything changing it.

i am curious how you intend to test ant change to the program if you cannot duplicate the problem right now.

Paul

Hey thanks for the interest in my problem!

This is the full code, it might be a bit cryptic though, but I’m happy to explain.
Also I’m pretty sure there are parts in the code that don’t make a lot of sense, but it’s still kind of the first try. So please feel free to also comment on parts that don’t have anything to do with the problem if you like.

My first thought was that the Raspberry could have interrupted the Arduino right when it was feeding (feedPin HIGH) and then it somehow got stuck? So right now I tried adding noInterrupts(); right at the beginning of feedcycle(); and and interrupts() at the end.
Could that be a solution? I’m running the code right now and I’ll let you know if something happens.

The variable feedcycles divides the daily food amount (which the user selects on the web-interface) by the amount of feedcycles a day. I noticed that if somehow the raspberry sends the code 110 (which it shouldn’t) there would be a division by zero. So i changed the code from:

      else if (code >= 110) {
          feedcycles = (code - 110); 
          last_code = code;  
      }

to:

      else if (code > 110) {
          feedcycles = (code - 110); 
          last_code = code;  
      }
#include <DHT.h>
#include <Wire.h>
#include <OneWire.h>
#include <DS18B20.h>
#include <SoftwareSerial.h>
#include <Servo.h>


//################# Pinbelegung ###################
#define DHTPIN 12
#define ONE_WIRE_BUS 4
SoftwareSerial phReader(2, 3);
#define hallPin A0
#define feedPin 5
#define servoPin 11
#define servoPowerPin 10

//#################################################

Servo feedServo;

#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

#define adresse 0x05

OneWire oneWire(ONE_WIRE_BUS);
DS18B20 waterTempSensor(&oneWire);

bool feedflag = false;
int code = 0;
int last_code = 0;
byte messwerte[] = {0,0,0,0,0,0,0,0,0,0,0};

int foodAmounts[] = {0,0,0,0,0};
int feedcycles = 1;
int servoPos[] = {0,70,0,115,180};
int weightfactor = 1748; //Für Zanderfutter ermittelt


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Wire.begin(adresse);
  Wire.onReceive(empfangeCode);
  Wire.onRequest(sendeMesswerte);

  waterTempSensor.begin();
  dht.begin();
  phReader.begin(9600);
  
  feedServo.attach(servoPin);
  feedServo.write(90);
  pinMode(servoPowerPin, OUTPUT);
  digitalWrite(servoPowerPin, HIGH);
  
  pinMode(feedPin, OUTPUT);
  digitalWrite(feedPin, LOW);
  
  Serial.println("Bereit");
}

void loop() {
    if (feedflag==true){
      feedcycle();
      feedflag = false;
    }
}


// ################################ Befehle in Form von Zahlenwerten von Raspberry empfangen ####################################
void empfangeCode(int byteCount) {
  while (Wire.available()) {
    code = Wire.read();
  }

// 0 ist Prüfziffer von Rpi, die beim Rpi vor jedem Emfang einmal geschickt wird und hier ignoriert werden soll    
    if (code == 0) {
      code = last_code;
    }
    else {
      if (code == 1) {
          Serial.println(" ");
          Serial.print("Code erhalten: ");
          Serial.println(code);
          feedflag = true;
          last_code = code;

      } 
      else if (code == 2) {
          Serial.println(" ");
          Serial.print("Code erhalten: ");
          Serial.println(code);
          Serial.println("Nehme Messwerte...");
          
          digitalWaterSensor();
          phController();
          dht11();
          last_code = code;
      }
      
      else if ((code >= 10) && (code<30)) {
          foodAmounts[0] = (code - 10);  
          last_code = code;
      }
      else if ((code >= 30) && (code<50)) {
          foodAmounts[1] = (code - 30); 
          last_code = code;
      }
      else if ((code >= 50) && (code<70)) {
          foodAmounts[2] = (code - 50); 
          last_code = code;    
      }
      else if ((code >= 70) && (code<90)) {
          foodAmounts[3] = (code - 70);  
          last_code = code;    
      }
      else if ((code >= 90) && (code<110)) {
          foodAmounts[4] = (code - 90); 
          last_code = code;    
      }
      else if (code >= 110) {
          feedcycles = (code - 110); 
          last_code = code;  
      }
    }
}

// ################################ Messwerte an Raspberry übermitteln ####################################
void sendeMesswerte() {
  Serial.println(" ");
  Serial.println("Raspi fragt Daten an, schicke Daten...");
  Wire.write(messwerte, 11);
  Serial.println("Daten gesendet! ");
  Serial.println("");
}

// ################################ Lufttemperatur und -feuchtigkeit messen ####################################
void dht11() {
    Serial.println("DHT11");
    float h = dht.readHumidity();
    float t = dht.readTemperature();
    if (isnan(h) || isnan(t)) {       
      Serial.println("Fehler beim auslesen des DHT11!");
      return;
    }
        
    Serial.print("Luftfeuchtigkeit: ");
    Serial.println(h);
    Serial.print("Lufttemperatur: ");
    Serial.println(t);

    messwerte[0] = int(h);
    messwerte[1] = floatSplitter(h);
    messwerte[2] = int(t);
    messwerte[3] = floatSplitter(t);

}

// ################################ Digitales Wasserthermometer ####################################
void digitalWaterSensor() {
    Serial.println("DigitalWasser");
    waterTempSensor.requestTemperatures();
    while(!waterTempSensor.isConversionComplete()); //Hier Funktion einbauen, dass er sich nicht aufhängt.
    float digital_water_temp = waterTempSensor.getTempC();
    if (isnan(digital_water_temp)) {       
      Serial.println("Fehler beim auslesen des digitalen Wasserthermometers!");
      return;
    }
    
    Serial.print("Wassertemperatur: ");
    Serial.println(digital_water_temp);

    messwerte[4] = int(digital_water_temp);
    messwerte[5] = floatSplitter(digital_water_temp);

}

// ################################ Ph-Controller ####################################
void phController() {
  Serial.println("PH-CONTROLLER");
  bool startRec = false;
  const byte startMarker = 0x29;
  char receivedChars[5];
  float ph = 0.0;
  float temp = 0.0;
  int i = 0;
  
  while (ph == 0.0 or temp == 0.0) {
    while (true) {
      if (phReader.available() > 0) {
        byte b = phReader.read();
        byte decod = ((0xFF - b) >> 1);

        if (startRec == true && i < 6) {
          receivedChars[i] = decod;
          i++;
        }
        else if (i >= 6) {
          startRec = false;
          i = 0;
          break;
        }

        if (decod == startMarker) {
          startRec = true;
        }
      }

      if (receivedChars[0] == 0x20) {
        temp = atof(receivedChars);
      }
      else {
        ph = atof(receivedChars);
      }

    }
  }
  Serial.println("Ph-Controller: ");
  Serial.print("Temp= ");
  Serial.println(temp);
  Serial.print("Ph= ");
  Serial.println(ph);

  if (isnan(temp) or isnan(ph) or ph == 0 or temp == 0){
    Serial.println("Keine Messwerte vom Ph-Controller empfangen!");
    return;
  }
  messwerte[6] = int(temp);
  messwerte[7] = floatSplitter(temp);
  
  messwerte[8] = int(ph);
  messwerte[9] = floatSplitter(ph);
  
}

// ################################ Fütterautomat ####################################
void feedcycle() {
  Serial.println("Futtermengen: ");
  for (int i = 0; i<=4; i++){
    Serial.print(foodAmounts[0]);
    if (i<4){
      Serial.print(", ");
    }
  }
  Serial.print("Fütterhäufigkeit: ");
  Serial.print(feedcycles);
  
  Serial.println(" ");
  Serial.println("Starte Fütterung");


  for (int i = 0 ; i<=4; i++){
    if (foodAmounts[i] != 0){
      digitalWrite(servoPowerPin, LOW); //LOW turns servo on (bought relais-board)
      feedServo.write(servoPos[i]);
      delay(1000);
      digitalWrite(servoPowerPin, HIGH);
      digitalWrite(feedPin,HIGH); // HIGH turns motor on (selfmade relais-board)
      delay((foodAmounts[i] * weightfactor) / feedcycles);
      digitalWrite(feedPin,LOW);
      delay(1000);
    }
  }
}


// ################################ Messwerte aufsplitten in ganze Zahlen und Nachkommawerte ####################################
int floatSplitter(float float_variable){
  int after_comma = (float_variable - int(float_variable)) * 100;
  return after_comma;
}

is there a lot of code sitting outside any function? Immediately after 0ist Prufziffer? Does the code do anything? the only function in void will not be called as feedflag is false

Ignore first bit, I just noticed it is part of the preceding function you just have some annotation

You overshoot your array. if i<=4 i++ means that i will equal 5. You don’t have a 5 in your array as it is indexed 0.

 int foodAmounts[] = {0,0,0,0,0};

for (int i = 0 ; i<=4; i++){
    if (foodAmounts[i] != 0){

Thanks pmagowan but I'm not sure i can follow. Why will i equal 5? And why does the code work non-stop for a few days and then crash?

Does anybody have a clue if it could have something to do with the I2C-Interrupts?

If i <= 4 i++

Means
If i <4 I.e 3210 fine
And
If i =4
Add 1

4+1=5

Working outside the bounds of an array will cause unexpected results. Remember arrays are zero indexed so [5] is the sixth element. 012345.

Remove the =

I’m a little confused, i might have a major misunderstanding here :smiley:

This code

  for (int i=0; i<=4; i++){
    Serial.println(i);
  }

gives me the output

0
1
2
3
4

and thats what i need to address all the 5 parts of the array, isn’t it?

Maybe I am confused and it doesn’t run the final addition.

This is ok for a five element array:

  for (int i=0; i<=4; i++){
    Serial.println(i);
  }

It’s more usually expressed as this, which I expect explains the cognitive dissonance here:

  for (int i=0; i<5; i++){
    Serial.println(i);
  }

Or better:

  for (int i=0; i<sizeof Myarray/sizeof Myarray[0]; i++){
    Serial.println(i);
  }