Go Down

Topic: This has me stumped! (Read 2561 times) previous topic - next topic

helichuck

A friend has a cow watering device in his field. The cows hit a lever with their nose, and get water.
I have a flow valve that will a switch when it senses that the valve opens, and I have a 12 volt alarm transducer.
I would like an alarm that would tell me if the valve got stuck and the water flows in excess of 20 minutes. The issue is that when the cows keep hitting the lever, the flow valve pulses on and off on and off, so I need ignore the pulses and only time when it stays on for an extended period.

Can this be done with the Arduino?

I really don't want to learn C++, I just want to help a friend out!
can anyone recommend someone to write this sketch for me?
Thanks,
 Chuck

DrAzzy

#1
Apr 09, 2016, 12:53 am Last Edit: Apr 09, 2016, 12:55 am by DrAzzy
Yeah, easy (well, depends a bit on how you get the signal back, and how far - you could make this hard if you're monitoring distant cows wirelessly or something)

If you described the parameters I mentioned we could give you a better idea of how big a deal this is. The simple cases are truly trivial.
ATTinyCore for x4/x5/x61/x7/x8/x41/1634/828/x313 megaTinyCore for the megaavr ATtinies - Board Manager:
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts, mosfets, awesome prototyping board in my store http://tindie.com/stores/DrAzzy

GoForSmoke

#2
Apr 09, 2016, 01:53 am Last Edit: Apr 09, 2016, 02:00 am by GoForSmoke
Arduino has a built-in time counter that tells milliseconds since board power-up and start.

It returns a 32-bit unsigned long number and can count up to 49.7-some days in milliseconds.
The sketch won't break after 49.7-some days, that is just the longest interval you can time using unsigned long milliseconds. Unsigned subtraction always delivers the difference between end and start. It's like how you know that 2PM - 10AM is 4 hours as is 7PM - 3PM.. you count from one to the other, on the clock 11 hours is the longest you can do that.

This only checks time if water flows. It sets the flow start time whenever flow starts.

Not compiled or tested:
Code: [Select]


unsigned long flowStartMillis;
const unsigned long flowDurationMillis = 20UL * 60000UL; // UL, make it unsigned long
byte waterIsFlowing;
cost byte waterFlowDetectPin = 2;

................

void loop()
{
  if ( digitalRead( waterFlowDetectPin )) // or however you detect water flow, this is an example
  {
    if ( waterIsFlowing == 0 )
    {
      waterIsFlowing = 1;
      flowStartMillis = millis();
    }
  }
  else waterIsFlowing = 0;

  if ( waterIsFlowing == 1 )
  {
    if ( mills() - flowStartMillis >= flowDurationMillis )  // now - start always == time difference
    {
      // code to send alarm and maybe turn the flow off goes here

      waterIsFlowing = 0; // if flow stays ON, this will make the alarm not repeat for 20 minutes
    }
  }
}
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

helichuck

Yeah, easy (well, depends a bit on how you get the signal back, and how far - you could make this hard if you're monitoring distant cows wirelessly or something)

If you described the parameters I mentioned we could give you a better idea of how big a deal this is. The simple cases are truly trivial.
The flow switch is not in the field, it is up in the house where this circuit will be.

helichuck

Hello Gofosmoke,

Thank you for writing this sketch for me.

This looks neat, I see where the input from the switch goes (pin 2)I guess that would be a 5volt signal?, But I don't see where the output to the alarm is! And would that be a 5 volt output?

Also I assume that to reset it after the alarm goes off, all you have to do is cycle the power??

Thanks, You ,
Chuck

GoForSmoke

#5
Apr 09, 2016, 03:07 am Last Edit: Apr 09, 2016, 03:09 am by GoForSmoke
Hello Gofosmoke,

Thank you for writing this sketch for me.

This looks neat, I see where the input from the switch goes (pin 2)I guess that would be a 5volt signal?, But I don't see where the output to the alarm is! And would that be a 5 volt output?

Also I assume that to reset it after the alarm goes off, all you have to do is cycle the power??

Thanks, You ,
Chuck
It resets itself after the alarm signal is sent (however that happens) by setting waterIsFlowing to 0. Next time through loop, if water is detected flowing it will set waterIsFlowing to 1 and set start time to then. If water stops flowing before 20 more minutes it will reset itself again.

Quote
Also I assume that to reset it after the alarm goes off, all you have to do is cycle the power??
What do you do if the cow lever is stuck or held down?

I don't have all the details of the system so I can't write it. How will you detect water flow? Can you clear the valve lever automatically? Can a shutoff be added at the site? I don't know, can't code for don't know.

Remember, loop() runs over and over. The code inside this one is like a wheel, goes around comes around.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

helichuck

I am Sorry, I thought it was clear in my first post.

The alarm is to let us know that the valve is stuck or there is a break in the line. When we hear the alarm, we know to come and shut off the water and look for the problem.

I don't know anything about code, so what you are telling me is all greek to me, I was just asking for some help. I will supply any information that you need.

Thanks for all of your help!

Chuck

PaulMurrayCbr

So, you want an alarm cancel button?

http://paulmurraycbr.github.io/ArduinoTheOOWay.html

PaulMurrayCbr

Ok. This sketch keeps a circular buffer of 40 30-second periods (20 minutes). If the total count exceeds the alarm trigger, then the alarm is triggered (duh). 

I am assuming that when the water switch is ON, then the digital read is LOW.

Over any 20-minute period, 1200 reading are taken. if 90% (1080) are LOW, then the alarm pin is set to HIGH. You can change this if you like.

Rather than keeping "is the alarm on?" in a variable, I just read the alarm pin.

This sketch contains provision for a 'turn off the alarm' switch. this should be a normally-open switch wired between the pin and ground (the usual arrangement).


Code: [Select]

byte waterSensorPin = 3;
byte alarmOffPin = 4;
byte alarmOutPin = 5;


const unsigned int sampleRateMs = 1000; // sample every 1000 ms
const int samplesPerPeriod = 30; // make 30 samples per perod, so 30 seconds
const int nPeriods = 40; // 40 thirty second samples is 20 minutes total

// if 90% of the samples over the total time are high, then sound the alarm
// make sure that samplesPerPeriod * nPeriods is not greater than 32767 !
const int alarmTriggerPoint = (int)(samplesPerPeriod * nPeriods* .9);

unsigned long mostRecentSampleMs = 0;
int samplesThisPeriod;
int period;

int countOfHigh[nPeriods];
int totalCountOfHigh;

void setup() {
  // put your setup code here, to run once:

  pinMode(waterSensorPin, INPUT_PULLUP);
  pinMode(alarmOffPin, INPUT_PULLUP);
  pinMode(alarmOutPin, OUTPUT);

  resetSamples();

}

void loop() {
  // put your main code here, to run repeatedly:

  if(digitalRead(alarmOutPin)) {
    // alarm is on
    if(digitalRead(alarmOffPin) == LOW) {
      digitalWrite(alarmOutPin, LOW);
      resetSamples();
    }
  }
  else {
    if(millis() - mostRecentSampleMs >= sampleRateMs) {
      takeASample();
      mostRecentSampleMs = millis();
      if(totalCountOfHigh >= alarmTriggerPoint) {
        digitalWrite(alarmOutPin, HIGH);
      }
    }
  }
}

void resetSamples() {
  memset(countOfHigh, 0, sizeof(countOfHigh));
  totalCountOfHigh = 0;
  period = 0;
  samplesThisPeriod = 0;
}

void takeASample() {
  if(digitalRead(waterSensorPin) == LOW) {
    totalCountOfHigh ++;
    countOfHigh[period] ++;
  }
  samplesThisPeriod ++;

  if(samplesThisPeriod >= samplesPerPeriod) {
    // advance circular buffer one step
    if(++period >= nPeriods) {
      period = 0;
    }
    // clear out the results that were recorded way back when
    totalCountOfHigh -= countOfHigh[period];
    countOfHigh[period] = 0;
  }
 
}
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

GoForSmoke

I am Sorry, I thought it was clear in my first post.
Sure. Minus a few details.

Quote
The alarm is to let us know that the valve is stuck or there is a break in the line.
So monitor the pump for being switched on for over 20 minutes, no need to mess with the valve?

Quote
When we hear the alarm, we know to come and shut off the water and look for the problem.
As long as you fix it in under 20 minutes the alarm won't sound again. That could be real handy if for some reason you miss it the first time.

The actual alarm code doesn't have to stop ringing right away, that's another detail -- how you want the alarm to behave and where it is and where the pump is 

As soon as you turn the pump off, the code will stop timing pump on-time.

Quote
I don't know anything about code, so what you are telling me is all greek to me, I was just asking for some help. I will supply any information that you need.

Thanks for all of your help!

Chuck
Tell it in descriptions of what someone would do if they did it all by hand. Try and get it down to the least number of steps. This is about how the system should act, not the code to do it.

I have so far:

1) check the pump to see if it's on or off

2) if it's on and last time it was off, start timing.

3) if it is on and time is up then give a yell and tell yourself it's off. You'll be checking again soon.

4) go to step 1
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

helichuck

Wow, this is getting complicated!
There is no pump.
There is just a water line that feeds the house and feeds the cattle watering device. Same line, same system!

I installed a flow switch in the line that senses when water is being used.

The water may turn on and off and on and off several times in any given period.
I just want to monitor the flow to see if it happens to stay on for a long time, indicating a stuck valve of a leak.

Then sound an alarm when that happens to let us know there is a problem.

Then we can turn off the alarm,fix the problem and start over again.

cedarlakeinstruments

@helichuck:
I live on a horse farm and I have a similar problem except it's human-caused. Someone turns on the water to fill the trough and forgets to turn it off until there's a major flood and a huge ice-patch in winter.

I eventually designed a timer to shut the water off, but when I was looking for a solution I came across water flow alarms that do what you need. You can buy these things online fairly inexpensively.

If you're getting confused by the messages here, send me a PM. I'm pretty sure I have some code that does what you need and I can send it to you.
Electronics and firmware/software design and assistance. No project too small

GoForSmoke

Wow, this is getting complicated!
There is no pump.
There is just a water line that feeds the house and feeds the cattle watering device. Same line, same system!

I installed a flow switch in the line that senses when water is being used.

The water may turn on and off and on and off several times in any given period.
I just want to monitor the flow to see if it happens to stay on for a long time, indicating a stuck valve of a leak.

Then sound an alarm when that happens to let us know there is a problem.

Then we can turn off the alarm,fix the problem and start over again.
Not a pump but a flow meter. Complicated? Changes the control scheme not at all. There will be code to read the flow meter instead of pump power switch, a detail. Small flow is probably a leak so all flow counts.

No crisis, only something that has to be fit in to work.

Take the horse system and do what you're told to set that up,it has to be easier than invention.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

PaulMurrayCbr

Quote
Hello Paul,
I have the sketch loaded, and here is what I have hooked up.

* I ground pin 3 to simulate flow
* I have an LED on pin 5 and ground to simulate the alarm
* I changed the periods to 2 so I can test it,

Nothing is happening.
I have a red light and a green light on the board that are on.
Any suggestions?
Do I have to do something to start it?
Hmm.

Well, you'll want:

* A button between pin 3 and ground to simulate flow (which you already have)
* An LED between pin 5 and ground to simulate the alarm (this should have a 220 ohm current limiting resistor
* A button between pin 4 and ground to reset the alarm

Changing the 'periods' to 2 means you get 2 30-second periods. Ill test it by changing the parameters like so:

Code: [Select]

const unsigned int sampleRateMs = 100; // sample every 1000 ms
const int samplesPerPeriod = 10; // make 30 samples per perod, so 30 seconds
const int nPeriods = 10; // 40 thirty second samples is 20 minutes total


which will give me a 10-second window.

I added some debugging - wait, wat? This is giving me some very unexpected stuff! Back soon …


http://paulmurraycbr.github.io/ArduinoTheOOWay.html

PaulMurrayCbr

#14
Apr 11, 2016, 02:30 am Last Edit: Apr 12, 2016, 03:24 am by PaulMurrayCbr
Gahh! I had two bugs.

The more subtle one was advancing the period after taking the sample. This mean that after taking the final sample of a period, the next block would get cleared, and so that final sample would almost never "count".

The one actually cauing the problem, however, was that I was not clearing "samples taken this period", so each period only got one sample and this (of course) meant that the limit never tripped.

Shows what happens when you don't test your code.

Code: [Select]

byte waterSensorPin = 3;
byte alarmOffPin = 4;
byte alarmOutPin = 5;

// To see debugging output, remove the comment on the next line
// #define DEBUG

// these constants are for testing. they give a 10-second cycle, taking 10 samples each second.
const unsigned int sampleRateMs = 100;
const int samplesPerPeriod = 10;
const int nPeriods = 10;

//const unsigned int sampleRateMs = 1000; // sample every 1000 ms
//const int samplesPerPeriod = 30; // make 30 samples per perod, so 30 seconds
//const int nPeriods = 40; // 40 thirty second samples is 20 minutes total

// if 90% of the samples over the total time are high, then sound the alarm
// make sure that samplesPerPeriod * nPeriods is not greater than 32767 !
const int alarmTriggerPoint = (int)(samplesPerPeriod * nPeriods* .9);

unsigned long mostRecentSampleMs = 0;
int samplesThisPeriod;
int period;

int countOfHigh[nPeriods];
int totalCountOfHigh;

void setup() {
  // put your setup code here, to run once:

  pinMode(waterSensorPin, INPUT_PULLUP);
  pinMode(alarmOffPin, INPUT_PULLUP);
  pinMode(alarmOutPin, OUTPUT);

  resetSamples();

#ifdef DEBUG
  Serial.begin(57600);
  while (!Serial);
  Serial.print("Sketch begins in ");
  for (int i = 3; i > 0; i--) {
    Serial.print(i);
    Serial.print("...");
    delay(1000);
  }
  Serial.println('0');
#endif
}

void loop() {
  // put your main code here, to run repeatedly:

  if (digitalRead(alarmOutPin)) {
    // alarm is on
    if (digitalRead(alarmOffPin) == LOW) {
      digitalWrite(alarmOutPin, LOW);
      resetSamples();
    }
  }
  else {
    if (millis() - mostRecentSampleMs >= sampleRateMs) {
      takeASample();
      mostRecentSampleMs = millis();
      if (totalCountOfHigh >= alarmTriggerPoint) {
        digitalWrite(alarmOutPin, HIGH);
      }
    }
  }
}

void resetSamples() {
  memset(countOfHigh, 0, sizeof(countOfHigh));
  totalCountOfHigh = 0;
  period = 0;
  samplesThisPeriod = 0;
}

void takeASample() {
  if (samplesThisPeriod >= samplesPerPeriod) {
    // advance circular buffer one step
    if (++period >= nPeriods) {
      period = 0;
    }
    // clear out the results that were recorded way back when
    totalCountOfHigh -= countOfHigh[period];
    countOfHigh[period] = 0;
    samplesThisPeriod = 0;
  }
 
  if (digitalRead(waterSensorPin) == LOW) {
    totalCountOfHigh ++;
    countOfHigh[period] ++;
  }
  samplesThisPeriod ++;

#ifdef DEBUG
  for(int i = 0; i<nPeriods; i++) {
    if(i==period) Serial.print('[');
    Serial.print(countOfHigh[i]);
    if(i==period) Serial.print(']');
    Serial.print(' ');
  }
  Serial.print(": ");
  Serial.println(totalCountOfHigh);
#endif

}
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

Go Up