Arduino interrupt returning unreliable values - arduino

I have a simple app to count water flux using a sensor that is equipped with a reed switch.
So the app should only count the number of times the switch closes.
My first code was:
const int sensorPin = 2;
volatile int counter = 0;
void setup() {
Serial.begin(115200);
pinMode(sensorPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(sensorPin), sensorISR, FALLING);
}
void loop() {
Serial.print("Counter: ");
Serial.println(counter);
}
void sensorISR() {
counter++;
}
And once a bottle of 20 liters was full the counter would show something like 120.
Then I changed the code as follows:
const int sensorPin = 2;
volatile int counter = 0;
void setup() {
Serial.begin(115200);
pinMode(sensorPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(sensorPin), sensorISR, FALLING);
}
void loop() { }
void sensorISR() {
counter++;
Serial.print("Counter: ");
Serial.println(counter);
}
And counter went down to 40 (using the same 20 liters bottle).
The count should be 20L but that is not my problem as it results from bouncing of the reed switch (I will address that latter).
As the project will have 3 sensors and 3 ISRoutines I wonder why putting the Serial.print() command into the main loop can result in such strange results.
Thanks
Paulo

Serial print statements rely on interrupts that are disabled during your ISR. So Serial.print statements don't belong in an ISR.
The reason your count went down is that now your ISR takes longer to execute and it covers up some of the bounce. There are innumerable tutorials on how to debounce something with an Arduino. You can surely find one.
The two easiest are to use a capacitor between the pin and ground for a hardware debounce or to just use millis or micros to note the time that an interrupt occurs and ignore any interrupts that occur within some small time of that.

Related

Arduino send bad signal to interrupt pin

I have connected coin hopper and coin acceptor to one arduino uno, coin acceptor connected to pin 2, coin hopper to pin 3 - sensor and pin 7 - relay. When coin hopper switch relay, it is executing coininterrupt
for coin hopper I am using this script link
coin acceptor script: link
I need this 2 scripts working on 1 arduino
my code:
#define SENSOR 3
#define RELAY 7
#define ACCEPTOR 2
volatile boolean insert = false;
int pulse=0,count;
char sen;
int temp=0;
unsigned long int timer;
void setup()
{
Serial.begin(9600);
pinMode(SENSOR,INPUT_PULLUP);
pinMode(RELAY,OUTPUT);
sen=digitalRead(SENSOR);
digitalWrite(RELAY, HIGH);
attachInterrupt(digitalPinToInterrupt(ACCEPTOR), coinInterrupt, RISING);
}
void loop()
{
if (insert) {
insert = false;
Serial.println("coin");
delay(1000);
}
if(Serial.available())
{
timer=millis();
// temp is amount to dispense send to arduino
temp=Serial.parseInt();
if(temp>0){
digitalWrite(RELAY,LOW);}
}
sen=(sen<<1)|digitalRead(SENSOR);
// if hopper sensor read drop coin
if(sen==1)
{
timer=millis();
pulse++;
sen&=0x03;
Serial.println("out 1");
//if dispensed coins equal with coins to dispense stop engine
if(pulse==temp)
{
digitalWrite(RELAY,HIGH);
pulse=0;
temp=0;
}
}
// if amount dispensed is not equal with amount to dispense and engine running, stop
if((digitalRead(RELAY)==LOW)&(millis()-timer>2000))
{
digitalWrite(RELAY,HIGH);
pulse=0;
temp=0;
}
}
void coinInterrupt() {
insert = true;
}
I was trying to change pins (arduino uno support interrupts on pin 2 and 3 only) but problem still appears so I guess there is issue in the code
your sketch does not run in this state :
first fix errors :
declare insert as volatile
remove cpulse (not used anywhere)
change 'if()' to (I suppose) 'if (insert) ....'
remove stuff with 'sen' var : simply use if(digitalRead(SENSOR)) or if(!digitalRead(SENSOR))
except if you need to store relay state.
use logical operators like || or && unless you really need bitwise operations
example of result sketch :
#define SENSOR 3
#define RELAY 7
volatile boolean insert = false;
byte amountToDispense = 0;
int pulse = 0;
int temp = 0;
unsigned long int timer;
void setup()
{
Serial.begin(9600);
pinMode(SENSOR, INPUT_PULLUP);
pinMode(RELAY, OUTPUT);
digitalWrite(RELAY, HIGH);
attachInterrupt(digitalPinToInterrupt(2), coinInterrupt, RISING);
}
void loop()
{
if (insert ) {
insert = false;
Serial.println("coin");
delay(1000);
}
if (Serial.available())
{
timer = millis();
temp = Serial.parseInt();
if (temp > 0) {
//amountToDispense = Serial.read() - 48;
digitalWrite(RELAY, LOW);
}
}
if (digitalRead(SENSOR))
{
timer = millis();
pulse++;
Serial.println("out 1");
if (pulse >= temp)
{
digitalWrite(RELAY, HIGH);
pulse = 0;
temp = 0;
}
}
if (!digitalRead(RELAY) && (millis() - timer > 2000))
{
digitalWrite(RELAY, HIGH);
pulse = 0;
temp = 0;
}
}
void coinInterrupt() {
insert = true;
}
What is this supposed to do?
sen=(sen<<1)|digitalRead(SENSOR);
You init sen with digitalRead(SENSOR);
Assuming that pin is LOW when you start the sketch and turns HIGH, sen will become 1.
Next you do sen &= 0x03 so sen is still 1.
Again sen=(sen<<1)|digitalRead(SENSOR); , sen will either be 2 or 3.
Next loop run sen=(sen<<1)|digitalRead(SENSOR); sen is now 4 or 6. and so on...
I don't have time to think about what you want to achieve but this is definitely a problem as you'll only enter if (sen == 1) once and never again.
If this is not sufficient you should probably improve your post as it is unclear what arduino sends bad signal to interrup pin is supposed to mean. That doesn't make sense. Explain the expected behaviour of your program and how it behaves instead. add more comments so it becomes clear what you intend to do with each block of code so we don't have to interpret

Arduino AccelStepper Library: Instant speed never reaches set speed

I am trying to develop a arduino code which runs a stepper motor with C# program via serial communication. I also use Accelstepper library, especially moveTo() and run() functions. I sent maxSpeed and step values as 3500 and 200.000 from C# and motor start to run immediately. I sure that it completes all steps, but after a while, I noticed that stepper motor never reaches its max Speed and it stuck at 3200-3300 range. So because of that finish time is increased. If I give steps more than 200.000, the gap between estimated finish time and real finish time is increased exponentially. If I sent speed as 1000, real speed more or less 970. I have to use acceleration function by the reason of needed torque. Then I search the problem and some people said that it occurs because of Accelstepper library which consist run() function and other stuff that I wrote in the loop section. Especially I could not ensure the reason of the problem is Arduino, AccelStepper library or code that I wrote. Can you please help me to solve problem?
NOTE: Arduino Mega 2560 is used.
Arduino code is below:
#include <AccelStepper.h>
#include <stdio.h>
#define STEP_PIN_C 5 //31
#define DIRECTION_PIN_C 23 //32
#define ENABLE_PIN_C 24 //33
#define SET_ACCELERATION 600.0
AccelStepper stepper(1, STEP_PIN_C, DIRECTION_PIN_C);
unsigned long oldTime=0;
unsigned long now;
float newSpeed;
float maxSpeed = 3500.0;
bool newDataBit, runAllowed = false,addingProg=false,mainProg=false;
char commandChar;
long currentPosition;
long int steps = 0, mainNewStep, addedNewStep,memMainStep;
void checkSerial();
void checkRunning();
void stopMotor();
void runMotor();
void sendInfo();
const unsigned long delayTime = 1000;
unsigned long timer;
int count = 0;
bool running = false;
void setup()
{
Serial.begin(9600);
pinMode(ENABLE_PIN_C, OUTPUT);
digitalWrite(ENABLE_PIN_C, HIGH);
stepper.setCurrentPosition(0); //initial value
stepper.setMaxSpeed(0.0); //initial value
stepper.setAcceleration(SET_ACCELERATION); //initial value
}
void loop()
{
sendInfo();
checkRunning();
checkSerial();
}
void checkRunning()
{
if (runAllowed == true)
{
if (stepper.distanceToGo() == 0)
{
stopMotor();
checkSerial();
}
else
{
runMotor();
checkSerial();
}
}
}
void checkSerial()
{
if (Serial.available())
{
newDataBit = true;
commandChar = Serial.read();
}
if (newDataBit == true)
{
///DoStuff depends on what is received as commandChar via serial port
mainProgram(stepper.currentPosition(),newSpeed,mainNewStep);
newDataBit = false;
}
}
void runMotor(){
digitalWrite(ENABLE_PIN_C, LOW);
stepper.run();
running = true;
}
void stopMotor(){
stepper.setCurrentPosition(0);
digitalWrite(ENABLE_PIN_C, HIGH);
stepper.stop();
running = false;
timer = millis() + delayTime;
}
void mainProgram(long currentPositionValue,float maxSpeedValue,long stepValue)
{
mainProg = true;
if (stepper.distanceToGo() == 0) //YOLUMU TAMAMLADIM
{
addingProg = false;
steps = stepValue;
stepper.setCurrentPosition(currentPositionValue);
//stepper.setSpeed(0);
stepper.setMaxSpeed(maxSpeedValue);
stepper.moveTo(steps);
}
else
{
steps = stepValue + steps;
stepper.setCurrentPosition(currentPositionValue);
//stepper.setSpeed(0);
stepper.setMaxSpeed(newSpeed);
stepper.moveTo(steps);
}
}
void sendInfo(){
now = millis();
if(now-oldTime > 1000){ //saniyede 1
Serial.print(stepper.currentPosition());
Serial.print(" ");
Serial.print(stepper.isRunning());
Serial.print(" ");
Serial.println(stepper.speed());
oldTime = now;
}
}
From AccelStepper documentation:
The fastest motor speed that can be reliably supported is about 4000
steps per second at a clock frequency of 16 MHz on Arduino such as Uno
etc.
This is if you do nothing else but running the stepper.
You check your serial interface and send multiple lines every second. Both is quite expensive.

Arduino infiniti tone

I am making a smoke detector.
When it detects smoke iz should alert with buzzer.
Is there any way I could make it to buzz forever until external interupt such as restart pin?
Or could I just remove timing from tone() function.
Here is the code I use.
int sensorValue;
int digitalValue;
int green = 8;
int red = 7;
void setup(){
Serial.begin(9600);
pinMode( 0, INPUT);
pinMode(green, OUTPUT);
pinMode(red, OUTPUT);
}
void start(){
digitalWrite(green, HIGH);
}
void loop() {
sensorValue = analogRead(0);
digitalValue = digitalRead(0);
Serial.println(sensorValue,DEC);
Serial.println(digitalValue,DEC);
delay(2000);
if(analogRead(0) < 100){
tone(9,200,1000);
digitalWrite(red,HIGH);
}
}
Playing a sound "forever" is straightforward:
if(analogRead(A0) < 100 ) {
tone(9,2000); // once triggered, will play the sound forever
}
To switch it off, you seem to like the RESET button. So there's no need to ever call
noTone(9);
BTW: what about reading the reference ?
There is lots of ways:
Change your logic that activate the buzzer.
while (analogRead(0) < 100){
tone(9,200,1000);
}
Just use an infinite loop:
while (1) {
tone(9,200,1000);
}
Reset the Arduino to get out of the infinite loop.
An variation on this would be to replace (1) with the code that checks a pin to exit the loop or reads the sensor.
if you're really bent on using interrupts
you didn't specify what board you're working with but
for uno the 2 3 pins can be attached as interrupts and just trigger a function that turns off the tone
check out this:
attachinterrupt

Making arduino only write when signaled (serial)

I'm trying to communicate between a Raspberry Pi and Arduino with USB serial, but I only want the Arduino to write when the RPI sends a signal.
My arduino code is as follows:
int sensorPin = A0;
int sensorValue = 0;
void setup(){
Serial.begin(9600);
}
void loop(){
sensorValue = analogRead(sensorPin);
if (Serial.available() > 0) {
Serial.read();
Serial.println(sensorValue,DEC);
Serial.flush();
}
}
Once i do one call from the RPI of:
serial.write('hey')
The arduino writes repeatedly. I thought Serial.available would return 0 most of the time because the buffer is cleared by the read, but it seems like it never gets cleared. I thought flush() might do it but it doesn't really have any effect.
That's odd.. Serial.read() should remove the bytes from the buffer after reading them.
Note: Keep in mind that Serial.read() only reads one byte at a time, this could be your issue since you're sending 'hey' from the Raspberry Pi it'll take 3 iterations of the loop the completely empty the buffer.
If this is not the issue you could try the serialEvent() function wich is called each time something arrives trough serial.
Your code would be like this:
int sensorPin = A0;
int sensorValue = 0;
void setup(){
Serial.begin(9600);
}
void loop(){
//Any other logic here
}
void serialEvent() {
sensorValue = analogRead(sensorPin);
Serial.read();
Serial.println(sensorValue,DEC);
}
By using the serialEvent() event, your loop looks cleaner. That's always nice.

Arduino - Using interrupts freezes processing and serial output?

So, the interrupts seem to work insofar as "interrupting" when an event happens. My only problem is that I the interrupts will occur 2-3 times and everything essentially stops (Serial out, everything).
I was programming the board to output serially a calculated distance based on the output of the HC-SR04 distance IC. The distances are calculated accurately but, like I said earlier, everything seems to freeze. Below is both the code and an image of the serial monitor.
#define TRIGPIN 4
#define ECHOPIN 3
#define RED 2
#define GREEN 13
#define INTNUM 1 //interrupt pin 1 is digital pin 3 on the duemilanove
#define PULSE 10 //microseconds
#define CYCLETIME 50 //milliseconds
void ledWrite(int), trigPulse(), getTime();
int millisNow, millisPrev = 0;
int microsPrev;
boolean isHigh = false;
void setup() {
Serial.begin (9600);
pinMode(TRIGPIN, OUTPUT);
pinMode(ECHOPIN, INPUT);
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
attachInterrupt(INTNUM, getTime, CHANGE);
}
void loop() {
trigPulse();
// some other code while waiting on HC-SR04 to interrupt us when echo goes HIGH
}
void trigPulse(){
if( (millisNow = millis()) - millisPrev >= CYCLETIME){ //sufficient cycle time
digitalWrite(TRIGPIN, HIGH);
delayMicroseconds(PULSE);
digitalWrite(TRIGPIN, LOW);
millisPrev = millisNow; //reset clock
}
return;
}
void ledWrite(int dTime){
int distance = dTime/58.2;
if (distance < 4) {
digitalWrite(RED,HIGH);
digitalWrite(GREEN,LOW);
}
else {
digitalWrite(RED,LOW);
digitalWrite(GREEN,HIGH);
}
if (distance >= 200 || distance <= 0){
Serial.println("Out of range");
}
else {
Serial.print(distance);
Serial.println(" cm");
}
}
void getTime(){
int timeNow = micros();
Serial.println("Interrupted");
if(isHigh == false){
microsPrev = timeNow; //get time now, pin LOW->HIGH
isHigh = true;
Serial.println("Returning ..");
return;
}
else { //pin HIGH->lOW
ledWrite(timeNow - microsPrev);
isHigh = false;
microsPrev = micros();
Serial.println("Returning ..");
return;
}
return;
}
I know this is an old thread, but I just came by it having my own problems. The problem here is that you cannot use:
Serial.Print()
Within an interrupt service routine. The reason that the Serial.Print() doesn't work within an ISR is that it uses interrupts to pull the characters out of the serial buffer, but interrupts of a certain level are masked within the ISR. What basically happens is that the arduino throws out all other interrupts that are of a lower priority, which Serial.Read() falls into.
It is documented in a number of places: link1, link2, link3
I think you are getting interrupt while you are already processing interrupt. You should try disabling the interrupt as soon as you are in interrupt function and re-enable it again when you are done processing just before return. Hence I would advice to have just one return so that you don't have to repeat code of enabling interrupt. Also make sure the function which you are calling inside your interrupt code do not re-enable the interrupt. It may happen that the function micros() or any of the Serial function are re-enabling the interrupt.
I would suggest instead of calling function directly in you interrupt code try using some flags and set/reset in interrupt and use these flags in main loop to call your regular function.

Resources