probably, the solution is stupid, but I'm struggling finding how to be able to count how many times a sensor value is above a specific threshold.
I'm using Arduino UNO and a laser pointer and a OPT101 light sensor to detect when the laser beam is pointed towards the sensor.
I have a flat perforated bar which is moved by a DC motor and I need to stop it every time the OPT101 sensor detects two consecutive holes.
When the laser beam goes through the hole and it is read by the OPT101, then the sensor outputs values above 750.
While the bar moves, the sensor readings go from 0-50 when no holes is detected and above 750 when the hole is found.
void loop() {
int opt101 = analogRead(A0);
if (opt101 > 750){
Serial.println("Hole detected!");
}
delay(5);
}
I hope someone can help me! Thank you!
EDIT: I need to stop counting as soon as I read a value above 750 since if the bar move slowly, the light sensor could stay above 750 longer than usual.
This is a state change detection problem. You have 2 states. Let's call them "no hole detected" and "hole detected". You simply need to count the number of times the state moves from "no hole detected" to "hole detected". When that is 2 then stop the motor. Now you just have to select reasonable thresholds to detect those states. It sounds you can base "no hole detected" on values less than some value and "hole detected" based on values greater than some value.
This is similar to performing state change detection with a digital input as described in the following tutorial:
You need to add to your logic a flag/boolean to tell you that you have detected that particular hole already and don't want to detect it again until the sensor reading has gone back to 50 or below. When it does go low, reset your flag/boolean so you can process the next detection.
I tried to change my code to reflect your tips in this way:
const int light_sensor = A0; // the pin that the pushbutton is attached to
const int ledPin = 13; // the pin that the LED is attached to
// Variables will change:
int buttonPushCounter = 0; // counter for the number of button presses
int hole_detected = 0; // current state of the button
int lasthole_detected = 0; // previous state of the button
void setup() {
// initialize the button pin as a input:
pinMode(light_sensor, INPUT);
// initialize serial communication:
Serial.begin(9600);
}
void loop() {
// read the pushbutton input pin:
hole_detected = analogRead(light_sensor);
// compare the hole_detected to its previous state
if (hole_detected != lasthole_detected) {
// if the state has changed, increment the counter
if (hole_detected > 750) {
// if > 750 than we found a hole!
buttonPushCounter++;
Serial.println("HOLE DETECTED! Holes Counter: ");
Serial.println(buttonPushCounter);
} else {
// if the current state is LOW then no hole
Serial.println("NO HOLE");
}
delay(20);
}
lasthole_detected = hole_detected;
if (buttonPushCounter % 2 == 0) {
// do something
} else {
// do something
}
}
The problem is that the hole counter continue to grow even if the bar is not moving.
For example, this is the output when the bar is not moving and the sensor is detecting a hole: the counter increments since it continues to detect the same hole.
NO HOLE
NO HOLE
NO HOLE
NO HOLE
HOLE DETECTED! Holes Counter:
1
HOLE DETECTED! Holes Counter:
2
HOLE DETECTED! Holes Counter:
3
HOLE DETECTED! Holes Counter:
4
HOLE DETECTED! Holes Counter:
5
You are re-detecting the hole whenever the light changes with any ADC noise. If you Serial.print(hole_detected); I'd bet you see different numbers each detection. Using just one threshold will make your system vulnerable to electrical/light noise. You can make it more robust by using two thresholds to give some hysteresis for latching between hole/not hole states.
I'd try a function that tracks when there is a hole and when there is not, and only counts up when it first sees a particular hole:
int checkHole(void) {
static int state = 0; // private state variable
if (state == 0 && analogRead(light_sensor) > 750)
{ // we found a hole!
state = 1;
buttonPushCounter++; // update global
}
if ( state == 1 && analogRead(light_sensor) < 50)
{ // no hole!
state = 0;
}
return state;
}
void loop() {
static int lastCount = 0 ;
checkHole();
if (buttonPushCounter != lastCount) {
Serial.println("NEW HOLE DETECTED! Holes Counter: ");
Serial.println(buttonPushCounter);
lastCount = buttonPushCounter;
}
if (buttonPushCounter % 2 == 0) {
// do something
} else {
// do something
}
}
Yes. My suggestion of using digitalRead() would also fix that. Why not try it out. You don't have to change the wiring to another pin, you can use digitalRead(light_sensor) and compare it to HIGH rather than 750.
I tried to use your code as you suggested, but it still increments the counter when it detects the first hole.
const int light_sensor = A0; // the pin that the pushbutton is attached to
const int ledPin = 13; // the pin that the LED is attached to
// Variables will change:
int buttonPushCounter = 0; // counter for the number of button presses
int hole_detected = 0; // current state of the button
int lasthole_detected = 0; // previous state of the button
int checkHole(void) {
static int state = 0; // private state variable
if (state == 0 && analogRead(light_sensor) > 750)
{ // we found a hole!
state == 1;
buttonPushCounter++; // update global
}
if ( state == 1 && analogRead(light_sensor) < 100)
{ // no hole!
state == 0;
}
return state;
}
void setup() {
// initialize the button pin as a input:
pinMode(light_sensor, INPUT);
// initialize serial communication:
Serial.begin(9600);
}
void loop() {
static int lastCount = 0 ;
checkHole();
if (buttonPushCounter != lastCount) {
Serial.println("NEW HOLE DETECTED! Holes Counter: ");
Serial.println(buttonPushCounter);
lastCount = buttonPushCounter;
}
if (buttonPushCounter % 2 == 0) {
// do something
} else {
// do something
}
delay(10);
}
This is the output that I get as soon as it finds the first hole and the bar is stopped:
NEW HOLE DETECTED! Holes Counter:
1
NEW HOLE DETECTED! Holes Counter:
2
NEW HOLE DETECTED! Holes Counter:
3
NEW HOLE DETECTED! Holes Counter:
4
NEW HOLE DETECTED! Holes Counter:
5
NEW HOLE DETECTED! Holes Counter:
6
NEW HOLE DETECTED! Holes Counter:
7
NEW HOLE DETECTED! Holes Counter:
8
NEW HOLE DETECTED! Holes Counter:
9
NEW HOLE DETECTED! Holes Counter:
I tried to use digitalRead() and I get 1 when there is the hole and 0 otherwise.
However, this doesn't change the fast that it still increments the counter as soon as it gets the first hole.
Aw, that was me -- with the double-equals, instead of assigning, it does a test and silently discards the result. I'll go up and edit my original error away.... I made the same mistake on the state = 0; transition as well.
I like these little state-machine functions for encapsulating features. You might extend it to track the pairs of holes, so it counts parts rather than holes.