How can i print something on the serial monitor only once - arduino

I have a button connect to my arduino board and i want the serial monitor to display pressed when the button is pressed and released when the button is not pressed.
My problem is that i want it to be print only once but with my code it prints it non stop.
I already tried writing the code in void setup but i cant seem to make it work.
Does anyone have any suggestions?
I would really appreciate the help.
const int pinButton = 8;
void setup() {
pinMode(pinButton, INPUT);
Serial.begin(9600);
}
void loop() {
int stateButton = digitalRead(pinButton);
if(stateButton == 1) {
Serial.println("PRESSED");
} else {
Serial.println("RELEASED");
}
delay(20);
}

this is my first answer on stack overflow.
Anyway, the solution I suggest is to save previous state of the button in another variable, compare it to the new state, if they are diffrent you print the message, else you don't.
Here's a code example :
const int pinButton = 8;
int previous_state;
void setup() {
pinMode(pinButton, INPUT);
Serial.begin(9600);
previous_state = digitalRead(pinButton);
}
void loop() {
int new_state = digitalRead(pinButton);
if(new_state == 1 && previous_state==0) {
Serial.println("PRESSED");
} if(new_state == 0 && previous_state==1) {
Serial.println("RELEASED");
}
previous_state=new_state;
delay(20);
}
This is not the optimal solution, but it should work. Chek out interruptions on Arduino to see how to do it better.

Related

Arduino interrupt button calls multiple ISRs

For a project I'm using an Arduino Mega2560 to control a couple of stepper motors with the AccelStepper library. It's the first time I'm using Arduino and I'm writing everything in Arduino IDE 2.0 (I also tried it with IDE 1.8 but no change). It's coming along quite nicely and it's a lot of fun! Everything seems to work as intended. Except for 1 thing.
I'm using 4 buttons to trigger certain events in my code, using the interrupt function. I'm doing this because the accelStepper library asks a lot of proccesing from the arduino and I need as many steps/sec as I can get. When 1 of the interrupts is activated it somehow calls another with it. I've removed everything from the code except the interrupt part to see if just the interrupts would work, but I still have the same issue. With this code I would expect to see a print in the serial of the button I pressed. However, when I press button one it sometimes prints both button 1 and button 2. When I press button 2 it always calls button 1 as well. Sometimes button 1 is printed first, sometimes button 2 is printed first. When I press button 3, button 2 is always called and when I press button 4, button 3 is always called.
As you can see in the code, I'm using port 18 through 21 as interrupt ports and their grounds are all connected to the same GND port on the Arduino. I tried disconnecting all the grounds and only connected button 1 with the Arduino GND. But somehow it still triggers button 2 as long as the cable of the button is still plugged into pin 19. Even though the ground of button 2 is not connected.
What am I doing wrong? Did I misunderstand how the interrupts with input_pullup works? Or am I doing something wrong in my code?
I'm running out of ideas so any help is appreciated!
// Define pins numbers
const int button1 = 18;
const int button2 = 19;
const int button3 = 20;
const int button4 = 21;
// Define variables
volatile bool button1Pressed = false;
volatile bool button2Pressed = false;
volatile bool button3Pressed = false;
volatile bool button4Pressed = false;
float timeTrigger1 = 0;
float timeTrigger2 = 0;
float timeTrigger3 = 0;
float timeTrigger4 = 0;
void setup() {
Serial.begin(9600);
Serial.println("Startup begins");
pinMode(button1, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button1), interrupt1, FALLING);
pinMode(button2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button2), interrupt2, FALLING);
pinMode(button3, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button3), interrupt3, FALLING);
pinMode(button4, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button4), interrupt4, FALLING);
Serial.println("Startup finished");
}
void loop() {
if (button1Pressed) {
Serial.println("button 1 was pressed");
button1Pressed = false;
}
if (button2Pressed) {
Serial.println("button 2 was pressed");
button2Pressed = false;
}
if (button3Pressed) {
Serial.println("button 3 was pressed");
button3Pressed = false;
}
if (button4Pressed) {
Serial.println("button 4 was pressed");
button4Pressed = false;
}
}
void interrupt1() {
if (millis() - timeTrigger1 >= 500) {
timeTrigger1 = millis();
button1Pressed = true;
}
}
void interrupt2() {
if (millis() - timeTrigger2 >= 500) {
timeTrigger2 = millis();
button2Pressed = true;
}
}
void interrupt3() {
if (millis() - timeTrigger3 >= 500) {
timeTrigger3 = millis();
button3Pressed = true;
}
}
void interrupt4() {
if (millis() - timeTrigger4 >= 500) {
timeTrigger4 = millis();
button4Pressed = true;
}
}
EDIT: checking the button state once the ISR is called did the trick. Like the code below.
void interrupt1() {
if (millis() - timeTrigger1 >= 500 && digitalRead(button1)==LOW) {
timeTrigger1 = millis();
button1Pressed = true;
}
}

Loop in simple arduino code is not working

I am working on simple project, arduino relay controled via mobile phone. I am trying to create a loop, which makes the relay switch between HIGH and LOW only with sending 1 to pin 7. I tried multiple variations but it never works, I am unable to find a mistake. HereĀ“s the code, thanks in advance.`
int relay = 7;
int prev; //previous value
int val; //actual value
void setup()
{
pinMode(relay, OUTPUT);
Serial.begin(9600);
}
void loop()
{
if(Serial.available()>0) //send data when recieved
{
val = Serial.read(); //read value
}
if (val == '1')
{
if (prev == '1')
{
digitalWrite(relay, LOW);
prev=val;
}
else
{
digitalWrite(relay, HIGH);
prev=val;
}
}
else if (val == '0');
{
if (prev == '0')
{
digitalWrite(relay, HIGH);
prev=val;
}
else
{
digitalWrite(relay, LOW);
prev=val;
}
}
}
Current code attempts to read the serial port, and if has something to read sets it into variable val.
In the case of where it has read a value, but doesnt have another value, val is still set to the same value as before.
So it then probally starts toggling the high/low transition as fast as the loop can go.
Instead, you could move the logic code to within the if(Serial.available()>0) statement so that it only gets called when the key is pressed.

arduino interrupts with servo motor

currently am working on project to open a door with access code using arduino UNO and a servo motor. Normal operation requires entering access code using keypad which is working fine. Another option requires pressing a button that causes an interrupt to rotate the servo motor. My problem is my interrupt only works once and never works again. Plus how do i put the for-loop to rotate the servo motor inside the interrupt function with a delay. I know that is not possible but am calling another function that has the delayMicroseconds but all this is not working. Below is my implementation please help
#include <Keypad.h>
#include <LiquidCrystal.h>
#include <Servo.h>
Servo servo;
const int openButtonPin = 2;
void setup() {
// put your setup code here, to run once:
servo.attach(5);
pinMode(openButtonPin, INPUT); //Pin 2 is input
attachInterrupt(0, enforceOpenAccess, HIGH); // PIN 2
}
void(* resetFunc)(void) = 0;
void loop()
{
//My other keypad implementations go here
}
void myDelay(int x) // function to cause delay in the interrupt
{
for(int i = 0; i<x; i++)
{
delayMicroseconds(1000);
}
}
void enforceOpenAccess() // ISR
{
for(int k =0; k<=180; k+=2)
{
servo.write(k); //rotate the servo
myDelay(30); //delay the rotation of the servo
}
}
The code above is run on arduino UNO being simulated in proteus and the interrupt button is a push button. Please if there is other ways of implementing that but with the same behaviour as I have described above help out. Thanks a lot
There are a couple of problems in the slice of code you posted. Just for completeness, you should post the loop function, since we can't guess what you wrote inside.
Just one comment: did you put a pullup? Otherwise use INPUT_PULLUP instead of INPUT for the button pinmode.
The main one is that you attached the interrupt for the HIGH mode, which will trigger the interrupt any time the pin is up, not on the rising edge. And please use the macro digitalPinToInterrupt to map to the correct pin:
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
Then.. Let's improve the code. You really should use the interrupts only when strictly necessary when you have to respond IMMEDIATELY (= less than a couple of milliseconds) to an input. Here you don't have to, so it's MUCH better to check for the button in the loop (more on turning the motor following)
uint8_t lastState;
void setup()
{
...
lastState = LOW;
}
void loop()
{
uint8_t currentState = digitalRead(openButtonPin);
if ((currentState != lastState) && (currentState == HIGH))
{
// Start turning the motor
}
lastState = currentState;
...
}
This will enable you to properly debounce the button too:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT); //Pin 2 is input
debouncer.attach(openButtonPin);
debouncer.interval(5); // interval in ms
}
void loop()
{
debouncer.update();
if (debouncer.rose())
{
// Start turning the motor
}
...
}
If, on the other way, you REALLY want to use the interrupts (because waiting for a couple of milliseconds is too much for you), you should do something like this:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT);
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
}
void loop()
{
...
}
void enforceOpenAccess() // ISR
{
// Start turning the motor
}
It looks like your code? No, because now we'll speak about turning the motor
You should NOT use delays to make steps, because otherwise you will wait for 30ms * 180 steps = 5.4s before being able to do anything else.
You can, however, make a sort of reduced state machine. You want your servo to move from 0 to 180 in steps of 1. So let's code the "don't move" state with any value greater than 180, and consequently we can do something like this in the loop:
unsigned long lastServoTime;
uint8_t servoPosition = 255;
const int timeBetweenSteps_in_ms = 30;
void loop()
{
...
if (servoPosition <= 180)
{ // servo should move
if ((millis() - lastServoTime) >= timeBetweenSteps_in_ms)
{
lastServoTime += timeBetweenSteps_in_ms;
servoPosition++;
if (servoPosition <= 180)
servo.write(servoPosition);
}
}
}
Then, using any of the previous examples, instead of // Start turning the motor write
lastServoTime = millis();
servoPosition = 0;
servo.write(servoPosition);
This way you won't block the main loop even when the button is pressed
This is what is in my loop()
char key = keypad.getKey();
if(key)
{
if(j < 10)
{
studentNumber[j] = key;
//holdMaskedNumber[j] = '*';
lcd.setCursor(0,2);
lcd.print(String(studentNumber));
if(j == 9)
{
studentNumber[9] = '\0';
//holdMaskedNumber[9] = 0;
lcd.clear();
//String number = String(studentNumber);
//lcd.print(number);
//delay(1000);
//lcd.clear();
lcd.print("Access Code");
}
j++;
}
else
{
if(i < 5)
{
accessCode[i] = key;
holdMaskedCode[i] = '*';
lcd.setCursor(1,2);
lcd.print(String(holdMaskedCode));
if(i == 4)
{
holdMaskedCode[5] = '\0';
accessCode[5] = '\0';
//lcd.clear();
//lcd.setCursor(0,0);
//accessCodeString = String(accessCode);
//lcd.print(accessCodeString);
//delay(1000);
lcd.clear();
for(int i =0; i<6; i++)
{
lcd.print("Please wait.");
delay(500);
lcd.clear();
lcd.print("Please wait..");
delay(500);
lcd.clear();
lcd.print("Please wait...");
delay(500);
lcd.clear();
}
digitalWrite(4, HIGH);
lcd.print("Access Granted");
for(int k =0; k<=180; k+=2)
{
servo.write(k);
delay(30);
}
resetFunc();
}
i++;
}
}
}

Arduino click, double click and hold button

I am trying to implement three different functions for one button in an Arduino project. Click, double click and hold.
I have to use interrupts and let the system sleep as much as possible, because the final product will have to run on a coin cell for a few months.
#include <Ports.h>
#include <RF12.h>
#include <avr/sleep.h>
#include <PinChangeInt.h>
#include <VirtualWire.h>
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
char *controller;
const int buttonPin = 3;
bool stateSingle = false;
bool stateDouble = false;
bool stateLong = false;
void setup() {
pinMode(13, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(5, OUTPUT);
// vw_set_ptt_inverted(true);
// vw_set_tx_pin(12);
// vw_setup(4000);
//
Serial.begin(9600);
PCintPort::attachInterrupt(buttonPin, wakeUp, HIGH);
}
void wakeUp() {
}
void loop() {
cli();
int i = 0;
while (digitalRead(buttonPin) == HIGH) { // Wait until button is LOW, or has been high for more than 600ms
Sleepy::loseSomeTime(50);
if (i > 12)
break;
i++;
}
if (digitalRead(buttonPin) == HIGH)
longTapAction();
else {
i = 0;
while (digitalRead(buttonPin) == LOW) { // Wait for possible double press
Sleepy::loseSomeTime(50);
if (i > 8)
break;
i++;
}
if (digitalRead(buttonPin) == HIGH) {
doubleTapAction();
while (digitalRead(buttonPin) == HIGH)
Sleepy::loseSomeTime(50);
} else
singleTapAction();
}
}
void singleTapAction() {
stateSingle = !stateSingle;
digitalWrite(5, stateSingle ? HIGH : LOW);
sei();
Sleepy::powerDown();
}
void doubleTapAction() {
stateDouble = !stateDouble;
digitalWrite(6, stateDouble ? HIGH : LOW);
sei();
Sleepy::powerDown();
}
void longTapAction() {
stateLong = !stateLong;
digitalWrite(7, stateLong ? HIGH : LOW);
sei();
Sleepy::powerDown();
}
The problem is that this is not always correctly working.
Because I'm using interrupts, millis() inside void loop() is not reliable, for some reason.
For any double click, and for any hold action, the single click function also gets called. I suspect this is due to multiple interrupts firing, but I have no way to test this. Also, sometimes, the double click seems to need only one click. Is my thinking wrong, did I forget something?
If you are seeing singleTapAction and doubleTapAction triggering too often, the problem could be that your method doesn't really debounce the button inputs, meaning you may read spurious noise on any click as a single press or double press. E.G your first while loop will exit almost immediately if there is a noisy input, which makes the following behavior difficult to predict.
https://www.arduino.cc/en/Tutorial/Debounce
If you have a look at the linked example on the arduino site - a possible solution is to record the period of time an input has been present and ignore any inputs of less than a certain period. Modifying your code to do this could stop the spurious calls.

Arduino Xbee Data parsing

Im sorry to make a post like this but i have tried everything and i cant get this working!
I have two arduinos hooked up with xbee's.
One is connected to my computer recieving data and the other is bettery powered and has a Wii nunchuck attached.
I know im getting good data from the nunchcuck cause i tested it without the xbee.
But i want to send the data over serial and recieve on the other to use for something else but doesnt seem to be working. Here is the code:
Arduino with wii:
#include <Wire.h>
#include <Servo.h>
const int vccPin = A3;
const int gndPin = A2;
Servo servo;
const int dataLength = 6; // Number of bytes to request
static byte rawData[dataLength];
enum nunchuckItems {
JoyX, JoyY, accelX, accelY, accelZ, btnZ, btnC};
void setup()
{
pinMode(gndPin, OUTPUT);
pinMode(vccPin, OUTPUT);
digitalWrite(gndPin, LOW);
digitalWrite(vccPin, HIGH);
servo.attach(9);
delay(1000);
Serial.begin(9600);
nunchuckInit();
}
void loop()
{
nunchuckRead();
int joyX = getValue(JoyX);
int joyY = getValue(JoyY);
Serial.print(joyX);
Serial.print(",");
Serial.print(joyY);
Serial.println();
}
void nunchuckInit(){
Wire.begin();
Wire.beginTransmission(0x52);
Wire.write((byte)0x40);
Wire.write((byte)0x00);
Wire.endTransmission();
}
static void nunchuckRequest(){
Wire.beginTransmission(0x52);
Wire.write((byte)0x00);
Wire.endTransmission();
}
boolean nunchuckRead(){
int cnt = 0;
Wire.requestFrom(0x52, dataLength);
while (Wire.available()){
rawData[cnt] = nunchuckDecode(Wire.read());
cnt++;
}
nunchuckRequest();
if (cnt >= dataLength)
return true;
else
return false;
}
static char nunchuckDecode(byte x){
return (x ^ 0x17) + 0x17;
}
int getValue(int item){
if (item <= accelZ)
return (int)rawData[item];
else if (item == btnZ)
return bitRead(rawData[5], 0) ? 0: 1;
else if (item == btnC)
return bitRead(rawData[5], 1) ? 0: 1;
}
How could i recieve this data on the recieving arduino?
Please help its for my school project!
Thank you!!
When you read the terminal (without the Xbee) do you see line with X,Y appear ? Because if your arduino terminal see it, the problem comes from the Xbee.
If your terminal see the line, look at your Xbee with Xctu. You must set the panID on both Xbee to see them communicate. you must also make the SL address of the sender equal to the DL address of the receiver (and same for the SH/DH).
Can you say us which Arduino, Xbee, shield you use. It can help us to have more details

Resources