Automatic Teat Sprayer for Cows - Unexpected behaviour from code

Hi there,

I have built an Automatic Teat sprayer for my cows which is spraying their udders as they walk through a race.

I am experiencing some unexpected behaviour as cows walk through the race. Below is a link to a video of it in action.

https://drive.google.com/file/d/1L3z3iUTIEJwf9aFysPEnQNrkyt1pSFZv/view?usp=sharing

There are 3 Retro-reflective photoelectric sensors positioned in the race (see attached image for diagram)

Intended operation:
A cow enters the race and breaks both sensor 1 and 2, a timer will start to calculate her speed.
As she moves through the race she will the break sensor 3, which stops the timer and calculates what speed she is traveling.
As she continues to move through the race when either Sensor1 or Sensor 2 is unbroken, depending on the speed calculation, relay 1 or relay 2 will open.

The issues I am having as you will see in the video is that when cows are following close behind each other the relay stays open and it will continuously spray. It will then be followed by the relay opening and shutting faster than normal.

I am not sure whats causing this so any help would be much appreciated. Code is below

const int sensor1 = 6;
const int sensor2 = 9;
const int sensor3 = 5;
const int relay1 = 4;
const int relay2 = 7;
int sensorState;

unsigned long previousMillis_1 = 0;
unsigned long previousMillis_2 = 0;        // will store last time LED was updated

// constants won't change:
unsigned long sprayinterval = 500;

boolean sensor1WasHigh = false;
boolean sensor2WasHigh = false;
boolean sensor3WasHigh = false;
boolean relay1On = false;
boolean relay2On = false;

boolean timingFlag = false;
unsigned long timerOff = 0;
unsigned long timerOn;
unsigned long  cowSpeed;
unsigned long cowTimer;


void setup() {
  pinMode(sensor1, INPUT_PULLUP);
  pinMode(sensor2, INPUT_PULLUP);
  pinMode(sensor3, INPUT_PULLUP);
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  Serial.begin(9600);
}

void loop() {
    unsigned long currentMillis = millis();   // capture the latest value of millis()
    

    // read the state of the Sensor 1,2 and set a flag if it is HIGH, Start timer to calculate cow speed:
    if (digitalRead(sensor1) == HIGH && sensor1WasHigh == false && digitalRead(sensor2) == HIGH && sensor2WasHigh == false)  {
        timerOn = millis();
        sensor1WasHigh = true;
        sensor2WasHigh = true;
        timingFlag = true;
        
        //Serial.print("timerOn: ");
        //Serial.println( timerOn );
    
    }

    // read the state of the Sensor 3, stop timer and set a flag if it is HIGH:
    if(digitalRead(sensor3) == HIGH && sensor3WasHigh == false && timingFlag == true)
      {
        timerOff = millis();
        timingFlag = false;
        sensor3WasHigh = true;
        cowTimer = timerOff - timerOn;
        //cowSpeed = 300UL / timerOff;
        Serial.print("Time: ");
        Serial.println( cowTimer );
        timerOff = timerOn;
        
      }
    //Fast Speed - Fire Relay 2
     if(cowTimer < 1000)
      {
        if (digitalRead(sensor1) == LOW && sensor1WasHigh || digitalRead(sensor2) == LOW && sensor2WasHigh && digitalRead(sensor3)== HIGH && sensor3WasHigh)  {
        
        sensor1WasHigh = false;
        sensor2WasHigh = false;
        sensor3WasHigh = false;
          
        // Sensor event here
           digitalWrite(relay2, HIGH);
           relay2On = true;
           Serial.println( "Fast Spray" );
        }
      }
      else{
    //Normal Speed - Fire Relay 1
    if (digitalRead(sensor1) == LOW && sensor1WasHigh || digitalRead(sensor2) == LOW && sensor2WasHigh && digitalRead(sensor3)== HIGH && sensor3WasHigh )  {
        
        sensor1WasHigh = false;
        sensor2WasHigh = false;
        sensor3WasHigh = false;
    
        // Sensor event here
           digitalWrite(relay1, HIGH);
           relay1On = true;
           Serial.println( "Normal Spray" );
    }
    } 
    
     if (currentMillis - previousMillis_2 >= sprayinterval) {
        
        digitalWrite(relay1, LOW);
        digitalWrite(relay2, LOW);
        relay1On = false;
        relay2On = false;
        previousMillis_2 = currentMillis;
        }
    

}

Would it be possible that when the cows are close together the cow detector does not detect 2 cows but one?

Put this globally

unsigned long currentMillis = millis();   // capture the latest value of millis()

That way current millis is not being updated with each loop so that an actual millis count can be exceeded

Put your millis update here

if (currentMillis - previousMillis_2 >= sprayinterval) {
        
        digitalWrite(relay1, LOW);
        digitalWrite(relay2, LOW);
        relay1On = false;
        relay2On = false;
        currentMillis = millis(); nd so on and so forth
        }

Thanks for those suggestions. I will make those changes and see if it improves.

Regarding it not detecting the second cow when they are walking close together. I don’t think that is the case, although it could happen, as the relay will only open if either sensor 1 or sensor 2 is rejoined after being broken. In this case it is opening but not closing.

You have some rather complex if/else logic in that code - please do an autoformat first (press - in the IDE) so at least the indentation is correct. It is too hard to follow this way.

I have the feeling that this logic can also be simplified by using a finite state machine rather than a bunch of if/else statements.

As this happens when cows walk too close together, that’s obviously where you have to start looking with your debugging. Add more Serial.print() commands to have a better idea on what your code is doing and where/when/how things break down.

Hi sorry about that. I have auto formatted

const int sensor1 = 6;
const int sensor2 = 9;
const int sensor3 = 5;
const int relay1 = 4;
const int relay2 = 7;
int sensorState;

unsigned long previousMillis_1 = 0;
unsigned long previousMillis_2 = 0;        // will store last time LED was updated

// constants won't change:
unsigned long sprayinterval = 500;

boolean sensor1WasHigh = false;
boolean sensor2WasHigh = false;
boolean sensor3WasHigh = false;
boolean relay1On = false;
boolean relay2On = false;

boolean timingFlag = false;
unsigned long timerOff = 0;
unsigned long timerOn;
unsigned long  cowSpeed;
unsigned long cowTimer;


void setup() {
  pinMode(sensor1, INPUT_PULLUP);
  pinMode(sensor2, INPUT_PULLUP);
  pinMode(sensor3, INPUT_PULLUP);
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  unsigned long currentMillis = millis();   // capture the latest value of millis()


  // read the state of the Sensor 1,2 and set a flag if it is HIGH, Start timer to calculate cow speed:
  if (digitalRead(sensor1) == HIGH && sensor1WasHigh == false && digitalRead(sensor2) == HIGH && sensor2WasHigh == false)  {
    timerOn = millis();
    sensor1WasHigh = true;
    sensor2WasHigh = true;
    timingFlag = true;

    //Serial.print("timerOn: ");
    //Serial.println( timerOn );

  }

  // read the state of the Sensor 3, stop timer and set a flag if it is HIGH:
  if (digitalRead(sensor3) == HIGH && sensor3WasHigh == false && timingFlag == true)
  {
    timerOff = millis();
    timingFlag = false;
    sensor3WasHigh = true;
    cowTimer = timerOff - timerOn;
    //cowSpeed = 300UL / timerOff;
    Serial.print("Time: ");
    Serial.println( cowTimer );
    timerOff = timerOn;

  }
  //Fast Speed - Fire Relay 2
  if (cowTimer < 1000)
  {
    if (digitalRead(sensor1) == LOW && sensor1WasHigh || digitalRead(sensor2) == LOW && sensor2WasHigh && digitalRead(sensor3) == HIGH && sensor3WasHigh)  {

      sensor1WasHigh = false;
      sensor2WasHigh = false;
      sensor3WasHigh = false;

      // Sensor event here
      digitalWrite(relay2, HIGH);
      relay2On = true;
      Serial.println( "Fast Spray" );
    }
  }
  else {
    //Normal Speed - Fire Relay 1
    if (digitalRead(sensor1) == LOW && sensor1WasHigh || digitalRead(sensor2) == LOW && sensor2WasHigh && digitalRead(sensor3) == HIGH && sensor3WasHigh )  {

      sensor1WasHigh = false;
      sensor2WasHigh = false;
      sensor3WasHigh = false;

      // Sensor event here
      digitalWrite(relay1, HIGH);
      relay1On = true;
      Serial.println( "Normal Spray" );
    }
  }

  if (currentMillis - previousMillis_2 >= sprayinterval) {

    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    relay1On = false;
    relay2On = false;
    previousMillis_2 = currentMillis;
  }


}

Its difficult (at least for me) to hold all that logic in my heat at one time. If this was my project I would have to draw a flow chart.

I suggest you look at Lucidchart. Its very easy and you can get a free limited account (limit is 3 documents and each has a limit of entries). For me its been a good tool. I not only get to see my project in a manner I can comprehend but I’ve found a number of areas I could simplify my code.

1 Like

John Rob
Thanks for that suggestion

Hi,
@JohnRob a good suggestion, there are so many combinations in this project, the flow of logic needs to be setout first.

Once a good flowchart has been worked out, I would then go to Pseudo code to begin rattling out the code structure.

Tom… :grinning: :+1: :coffee: :australia:

Thanks for the replies @JohnRob & @TomGeorge

I will work on a flow chart and post back.

Cheers

The timing is hard to understand. It looks to me though that previousMillis_2 is used to time how long the chosen spray relay is on. If that is true, I think it needs to be set when spraying starts and from what I can see, it isn’t.

Better names would help me at least, to understand what’s happening.

Your video isn’t accessible either - that would probably bring some more clarity.

Hi All,

Thank you very much for your input so far, I really appreciate it.

I’ve had an attempt at a flow diagram however having no experience with this I am unsure if I my attempt will make sense to anyone other than myself.

I am unsure how to represent the following:

  • Both sensor 1 and sensor 2 needing to read HIGH before the timer starts
  • Before spraying either sensor 1 or 2 need to read LOW & the sensorwashigh flag = true & sensor 3 read HIGH

I am hoping this is enough of a starting point and I can clarify any questions.

Thanks again,
Charles

I have started to draw a flowchart in LibreOffice Draw. It seems usable. With some more usage I’ll see if it is as good as LucidChart.

John

Sir anything related to the relay is causing any issue, like the supply voltage and the current from it. I have some doubt over that too.

Hello
my proposal works with a different setup of sensors. I will not work with the speed of walking cow. My intentions is to measure the length of a cow using a double photoelectric sensor. This will have the follwing logic:

SensorA | Sensor B || comment
----0-------|-----0------- || now cow in range → do nothing
----1-------|-----0------- || start of cow → do nothing
----0-------|-----1------- || end of cow → sprayer on for time x
----1-------|-----1------- || mid af cow → do some preparation if nessarry

The arrangement, design and layout of the double photoelectric sensor depends on the environmental condition you have.

Hi,

Thanks for your message.

In my current configuration there are 2 spray nozzles. A standard nozzle for walking cows and a high volume nozzle for fast moving cows to ensure they are still sprayed. The speed calculation is to determine which nozzle they are sprayed with.

Sensor 1 and 2 are position on a vertical line at the same point in the race. I have 2 sensors here to avoid a false spray from a cows head dipping through the sensor as she enters.

Sensor 3 is set 600mm down the race and is used to calculate the speed but also determind the object moving through the race is large.

Once sensor 1 or sensor 2 return low from high and sensor 3 is high. The nozzle will spray. The vertically set sensor 1 and 2 will also reduce the chance of cows moving through the race close together and there being no gap.

I can see after typing this that sensor 3 can replace sensor 2s function to avoid cows dipping their heads.

I would have thought that you could do a reasonable job with a sensor just before the sprayer. When it sees the cow disappear, you’re pointing at the tail and it’s time to spray.

If necessary, record the time that sensor saw the cow to calculate speed and choose the sprayer as appropriate.

Hello
and many thanks for your reply and system description. You may build a small “cow race” mock up to study ‘all’ possible system states by using Play Mobil cows. Might be it usefull to provide names for the sensor1, sensor2 and sensor3 referencing their location and/or task?

Cow bodies… What about a vertical line of sensors maybe 8 or 10 or so. Could even be light detectors. You average them all together to give you a wave form that has high blobs and low blobs. Even if the rear cow gets close, you can see a lower blob because the neck and head are much less area than the cow body.

Two sensor arrays one to measure the length of the incoming cow blob and the second to actually fire the spray.

Kinda’ the same logic, but hopefully less error prone?

-jim lee

Well heck might as well use Linear Regression, and ML to create a mathematical model of what is a cow and use the training model to detect “COW”. Even better use a Raspberry Pi and TensorFlow to ‘say’ “COW” and even “COW TEAT” time to spray.

Don’t forget the lasers to map each udder and automatically attach the milking machine :grinning:

IIRC, I did see a documentary about this very thing. I’m not sure if it was really production ready though.