Counting "In" and "Out"

Hi,

This is my first program and first post so go easy on me! I am trying to count pills through a tube using two Photodiodes/IR LED. I am testing this program with two push buttons before I move to the true sensors. What will happen is pills will slide (gravity) down a tube past the pair of sensors, the sensors will be close enough together to be both covered with just 1 pill. I am not sure how fast the pills will past, probably some fast some slow but I think I can add timing/conditioning to the program later. At the moment I am trying just to get the theory right. The below does not compile because of the second IF conditions, where it looks for the direction indicator (sensor going LOW) Can someone help to confirm my theory and clues to how I would code this.
Later on I need to count maybe up to 30 tubes of passing pills, would be interested to hear schemes on how this would be possible....or not as the case maybe.

Thanks for any help
Simon

/*
Reads a digital input on pin 2 and pin 3,
determine direction and increment counter,
print counter to the serial monitor

Code copyright Simon Miller
*/

// Assign digital pins:
const int gateFront1 = 2;
const int gateBack1 = 3;
const int gate1Led = 13;

// Assign variables
int gateFront1state = 0;
int gateBack1state = 0;
int lastgateFront1 = 0;
int lastgateBack1 = 0;
boolean trigger = false;
unsigned long timeChange;
int pillOut = 0;
int pillIn = 0;
int pillCount = 0;

// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
// make the pushbutton's pin an input:
pinMode(gateFront1, INPUT);
pinMode(gateBack1, INPUT);
pinMode(gate1Led, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
pillCount = pillOut - pillIn;
// read the pushbutton input pin:
gateFront1state = digitalRead(gateFront1);
gateBack1state = digitalRead(gateBack1);

// check for both sensors being triggered
if (gateFront1 == HIGH && gateBack1 == HIGH) {
trigger = true;
timeChange = millis();
digitalWrite(gate1Led, HIGH);
}
else {
digitalWrite(gate1Led, LOW);
trigger = false;
}

if (gateFront1 == LOW && trigger = true)
{
pillIn ++;
}
else (gateBack1 == LOW && trigger = true)
{
pillOut ++;
}

// print results to serial
// Serial.print("number of pills in: ");
// Serial.println(pillIn);
// Serial.print("number of pills out: ");
// Serial.println(pillOut);
// Serial.print("delta pills: ");
// Serial.println(pillCount);
}

  if (gateFront1 == LOW && trigger = true)
  {
    pillIn ++;
  }
  else (gateBack1 == LOW && trigger = true)
  {
    pillOut ++;
  }

I'm curious why you are mixing equality operator (==) and assignment (=) in the same statement? Doing so is wrong, of course.

Proper usage of if/else is as follows:

if(condition){}
else{}

If you want a second condtion, use:

if(condtion1){}
else if(condtion2){}

Why do you need two sensors? I'd have though you could just detect transitions on a single one and just increment your pill count when it transitions high.

wildbill:
Why do you need two sensors? I'd have though you could just detect transitions on a single one and just increment your pill count when it transitions high.

It looks like the two sensors are to determine which direction the pill is coming from, based on the order in which the sensors are triggered.

jraskell:

wildbill:
Why do you need two sensors? I'd have though you could just detect transitions on a single one and just increment your pill count when it transitions high.

It looks like the two sensors are to determine which direction the pill is coming from, based on the order in which the sensors are triggered.

They're falling down a tube. Direction should be a constant. Keeping touching pills counted separately might be more of a problem.

It looks like the two sensors are to determine which direction the pill is coming from, based on the order in which the sensors are triggered.

If the fall down by gravity (the OP's description)? But you're right, that the code let us assume it's meant like that.

The problem is the counter will never count because the second and third if will never get true. If both sensors are HIGH, trigger gets true but the second condition (that one of the two sensors is LOW) cannot match as both must be HIGH (first if). So go to the next loop, let's say sensor Front gets low. The first if condition is false, the else clause is executed, where trigger gets false and again both other ifs will never get executed because they test for a true trigger value.

I guess what OP means is:

// check for both sensors being triggered
  if (gateFront1 == HIGH && gateBack1 == HIGH) {
    trigger = true;
    timeChange = millis();
    digitalWrite(gate1Led, HIGH);
  }   
  else {
    digitalWrite(gate1Led, LOW);
  }
   
  if (gateFront1 == LOW && trigger == true)
  {
    pillIn ++;
    trigger = false;
  }
  else (gateBack1 == LOW && trigger = true)
  {
    pillOut ++;
    trigger = false;
  }

This assumes the code is fast enough to get a state where only one sensor gets LOW after being HIGH and not both together.

Do the pills ever touch? Are they round?

If so then there is space between to the side of center where you have pill then no pill. To detect just that, put a small aperture in front of the sensor, a pinhole through some tape even. You can get by with one sensor then.

PaulS ... Yes you are correct, that's my mistake should be trigger == true

Yes I do need to detect direction, the pills are actually short lengths of bar (10mm -12mm) that travel in both directions but not at high speed, probably maximum 0.5 to 1.0 sec to pass the sensors. Very unlikely to be two pills back to back.
Simon

If likely is good enough for you...

Are the ends rounded?

No the ends aren't rounded. If rounded, what impact would this have?
I said "unlikely" because if they are touching, a count of one rather than two would be acceptable based on the probability and the overall large count numbers, percentage wise well within tolerance. I want to make the two sensors being triggered together as the start then within say for 0.5sec look for a either of the sensors going low to confirm a count and its direction. Think this is a sound theory but struglling to code it....

I have Tylenols that are oval shape is why I ask. Two end to end, there is a gap at each side. It is small but that's no big deal. You could run whole chains of those through and get the right count.

.5 sec is a very very long time for Arduino. You can be checking 20-100 times a millisecond if you arrange your code right. Then, being sure of a gap might mean 4+ checks in a row showing HIGH and many more in a row LOW means there is a pill -- in under a millisecond. That would be a better way to guard against false data from spikes or whatever might happen. It would also allow pills to be run faster and closer, but I guess the system doesn't want pills impacting, chipping or rubbing the tube.

I would use state code for this application. But first, be sure what you will do and how.

What do you mean by "State code for this application"?

The pills are passing very slowly, I don't think the code below will work without some "time" conditioning.
I think I need to post this thread into "Project Guidance" or "Sensors" to get my theory straight first. Should I start a new thread in there?

void loop() {
pillCount = pillOut - pillIn;
// read the pushbutton input pin:
gateFront1state = digitalRead(gateFront1);
gateBack1state = digitalRead(gateBack1);

// check for both sensors being triggered
if (gateFront1 == HIGH && gateBack1 == HIGH) {
trigger = true;
timeChange = millis();
digitalWrite(gate1Led, HIGH);
}
else {
// digitalWrite(gate1Led, LOW);
trigger = false;
}

if (gateFront1 == LOW && trigger == true)
{
pillIn ++;
}
else if (gateBack1 == LOW && trigger == true)
{
pillOut ++;
}

As I mentioned in my previous post: your code has a logical error and will not work as it is. Take a look at the code I posted and compare to your's.

You need to post code in code tags. When you go to post and the post window is open (what I use to type this reply) look above the text entry space for a button with # on it and click that. It makes the html code tags. You can paste your code between and it will be 100% readable on forum.

Have you run any tests with even 1 sensor to see how many reads a pill makes as it passes? Hint: don't have the serial monitor print every read out as that will slow the code tremendously.

This is NOT tested. It is just to give an idea of one test:

void loop() {
  static unsigned long count = 0UL; // unsigned long because I expect BIG numbers
  byte gate;

  // won't work with a push button but you can drop objects or wave fingers between a light and sensor to test
  gate = digitalRead(gate);

  if (gate = HIGH) // nothing is between the light and the meter
  {
    if (count > 0UL)
    {
      Serial.println(count); // should tell how many times each object was 'seen'
    }
    count = 0UL;
  }
  else  count++;
}

Something like this will tell the difference between thought and reality.
It is best to test simple actions before deciding how the code should be.
In the above, count -may- roll over if you hold something in the way long enough.

Hi,

I have finally got some spare time to make some more progress on the counter. I have got the IR LED/Sensor to successfully count objects passing counting both up and down. This is the first piece of code so I am sure it is not very elegant or even the right way to achieve the goal, but it seems to work reliably.

My next task is to build a housing to test this in its proper environment.

In addition to this I would like to interrogate the data I collect for things like "objects out in the 'n' hours", peek 'in' objects per hour, graphs in Excel would be great. Can anyone give me some clues to where I should concentrate my learning?

Simon

/*

 Reads a digital input from IR LED on 2-3 and switches LED on 11-12
 
 This example code is in the public domain.
 */

// variables
// int testLed;
unsigned long aTime;
int a1Trigger = LOW;
int a2Trigger = LOW;
int ATriggerState = HIGH;
int ATriggerLast = HIGH;
int gateACount = 0;

// the setup routine runs once when you press reset:
void setup() {
  // set pins as input or outputs
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input pin:
 
  gateA();  // call gate1 routine
  // delay(20);
  // Serial.print("number of objects:  ");
  Serial.println(gateACount);
 // Serial.print("Time:  ");
//  Serial.println(aTime);
 // Serial.print("Varables:  ");
 // Serial.println(a1Trigger);
 // Serial.println(a2Trigger);
 // Serial.println(ATriggerState);
 // Serial.println(ATriggerLast);
}

void gateA() {
   a1Trigger = digitalRead(2); // read IR Sensor a1
   a2Trigger = digitalRead(3); // read IR Sensor a2
   
   if (a1Trigger == LOW && a2Trigger == LOW) {
     ATriggerState = LOW;
     aTime = millis();  // Start 'a' gate timer
     digitalWrite(11, LOW);
     digitalWrite(12, LOW);
  }
  else { ATriggerState = HIGH;
  digitalWrite(11, HIGH);
  digitalWrite(12, HIGH);
 }
 // delay (20);
 
 if (a1Trigger == HIGH && a2Trigger == LOW) {
   if (ATriggerLast != ATriggerState) {
   gateACount ++;
   digitalWrite(11, LOW);
  // delay(3);
  }
 } 

 if (a2Trigger == HIGH && a1Trigger == LOW) {
   if (ATriggerLast != ATriggerState) {
    gateACount --;
    digitalWrite(12, LOW);
  // delay(3);
  }
 }
  ATriggerLast = ATriggerState; 
//}
}