i loaded a basic program to my Arduino Leonardo:
void setup() {
// make pin 2 an input and turn on the
// pullup resistor so it goes high unless
// connected to ground:
pinMode(2, INPUT_PULLUP);
Keyboard.begin();
}
void loop() {
//if the button is pressed
if(digitalRead(2)==LOW){
//Send the message
Keyboard.print("Hello!");
}
}
This example works, but it generate a infinite loop printing "Hello!". How can i control the loop?
The basic example is: http://arduino.cc/en/Reference/KeyboardPrint
Thanks !
If you want to say hello whenever the button transitions from off to on, you need to remember the previous state in a variable.
Then you only say hello when the current state is pressed and the previous state was not-pressed.
That would be something like:
int curr, prev = HIGH;
void loop () {
curr = digitalRead (2);
if ((prev == HIGH) && (curr == LOW)) {
Keyboard.print("Hello!");
}
prev = curr;
}
Related
I am trying to switch on and off the Arduino via a button. I used tutorial https://www.instructables.com/id/A-Guide-to-Putting-Your-Arduino-to-Sleep/ in order to get an idea how to do so. But unfortunately, the scenario does not fit 100%. I want both, on and off via the same switch (not by timer).
So I have implemented it the way shown below, just re-assigning different functions to the event via attachInterrupt().
Its like you cannot re-assign the function once assigned to an event. Is that the case? Does someone have a solution for that problem?
I already added detachInterrupt before using attachInterrupt again. Also I have already added a delay in between. Tried interrupt event HIGH, LOW and CHANGE, always same result, goes to sleep but never wakes up again.
#include <avr/sleep.h>
#define INTERRUPT_PWR_BUTTON_PIN 2 // pin for power button (Power button)
void setup() {
Serial.println("I'm up...");
attachInterrupt (digitalPinToInterrupt(INTERRUPT_PWR_BUTTON_PIN), goToSleep, HIGH); //attaching wakeup to interrup 0 on pin 2
delay(1000);
}
void loop() {
Serial.println("Ping");
delay(1000); // wait 1 sec
}
void goToSleep() {
Serial.println("Power Button pressed...");
Serial.flush();
sleep_enable(); // only enabling sleep mode, nothing more
detachInterrupt(digitalPinToInterrupt(INTERRUPT_PWR_BUTTON_PIN)); //remove interrupt 0 from pin 2
attachInterrupt (digitalPinToInterrupt(INTERRUPT_PWR_BUTTON_PIN), wakeUp, HIGH);
/* 2019-05-02 NN: Does not work, device will not wake up at all, unless pressing reset -> 2 buttons for now */
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // setting sleep mode to max pwr saving
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
sleep_cpu();
Serial.println("Just woke up!"); // gets executed after interrupt
digitalWrite(LED_BUILTIN, HIGH);
}
void wakeUp () {
Serial.println("Wakeup Interrupt Fired");
sleep_disable();
detachInterrupt(digitalPinToInterrupt(INTERRUPT_PWR_BUTTON_PIN)); //remove interrupt 0 from pin 2
attachInterrupt (digitalPinToInterrupt(INTERRUPT_PWR_BUTTON_PIN), goToSleep, HIGH);
}
As you want to use only one button, you can trigger toggle function via interrupt and from that function make Arduino sleep or wake. You have to use one boolean flag to achieve this.
...
// use flag to control state of sleep
bool isSleeping = false;
void setup() {
...
attachInterrupt (digitalPinToInterrupt(INTERRUPT_PWR_BUTTON_PIN), toggleSleepState, HIGH); //attaching wakeup to interrup 0 on pin 2
...
}
void loop() {
...
}
void toggleSleepState() {
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// If interrupts come faster than 200ms, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > 200)
{
// toggle state of sleep
isSleeping = !isSleeping;
if (isSleeping == true) {
goToSleep();
}
else {
wakeUp()
}
}
last_interrupt_time = interrupt_time;
}
void goToSleep() {
// sleep logic
}
void wakeUp () {
// wake up logic
}
Due to a shortage of pins on a esp8266 in arduino, I need a way to detect a button where;
momentary press runs snooze()
15 sec press runs conf_Desk()
30 sec press runs calibration()
the preconfig;
int buttonPin = D7;
pinMode( buttonPin , INPUT_PULLUP);
All while allowing the main loop to function.
If I trap an interrupt it stops cycling the loop(), a few millisec delays are OK but seconds of delay is too much.
The functions are already written I just can't seem to come up how to track and confirm the hold length to call the right function based on the right timing without stopping other process that must stay cycling.
Using an interrupt is, IMHO, overkill. Interrupts are made for when you need to reply to a stimulus quickly, and a button press is something slow. Unless your loop is blocking, thing that I highly discourage.
ADDITION: as Patrick pointed out in the comments, there is in fact another reason to use interrupts: sleep mode. In fact, if you want to go into sleep mode and wake with a button, you have to use interrupts to wake later. However usually you have to do something continuously and not only reply to the button inputs. If you can't go into sleep mode, using an interrupt for button detection is still overkill in my opinion.
So, if you properly designed your loop not to block, here is a brief part of code doing what I think you should implement:
uint8_t buttonState;
unsigned long lastPressTime;
void setup()
{
...
buttonState = digitalRead(buttonPin);
lastPressTime = 0;
}
void loop()
{
uint8_t currRead = digitalRead(buttonPin);
if (buttonState != currRead)
{ // Button transition
buttonState = currRead;
if (buttonState == LOW)
{ // Button pressed, start tracking
lastPressTime = millis();
}
else
{ // Button released, check which function to launch
if (lastPressTime < 100)
{} // Discard (it is just a bounce)
else if (lastPressTime < 15000)
snooze();
else if (lastPressTime < 30000)
conf_Desk();
else
calibration();
}
}
...
}
Since you made three very distant intervals though, I think that this part better suits your needs:
if ((lastPressTime > 100) && (lastPressTime < 7000))
snooze();
else if ((lastPressTime > 12000) && (lastPressTime < 20000))
conf_Desk();
else if ((lastPressTime > 26000) && (lastPressTime < 40000))
calibration();
So you define validity ranges, so if someone presses the button for 10 seconds nothing happens (this is useful because if someone presses the button for 14.9 seconds in the previous code it will trigger the snooze function).
i would use a simple state machine structure with two global vars to avoid complex nested logic:
int buttonDown = 0;
unsigned long buttonStart;
void loop(){
int snapshot = digitalRead(buttonPin);
if(!buttonDown && snapshot ){ //pressed, reset time
buttonDown = 1; // no longer unpressed
buttonStart = millis(); // when it was pressed
}
if(buttonDown && !snapshot ){ //released, count time
buttonDown = 0; // no longer pressed
int duration = millis() - buttonStart; // how long since pressed?
// now the "event part"
if(duration>30000) return calibration();
if(duration>15000) return conf_Desk();
snooze();
}
sleep(1); // or whatever
}
Interrupt service routines should be a short as possible. You don't have to wait inside the ISR and suspend your main loop for seconds.
Just use two different ISRs for a rising and a falling edge.
When the button is pressed, ISR1 starts a timer, when it is released ISR2 stop it and triggers whatever is necessary depending on the time passed.
Make sure your button is debounced.
https://www.arduino.cc/en/Reference/attachInterrupt
Another way to do this is with a pointer-to-function based state machine.
The advantage on this is that you can easily introduce more functionalities to your button (say, another function called at 45 seconds).
try this:
typedef void(*state)();
#define pressed (millis() - lastPressed)
void waitPress();
void momentPress();
void shortPress();
void longPress();
state State = waitPress;
unsigned long lastPressed;
int buttonState;
int buttonPin = 7;// or whathever pin you use
void snooze(){} // stubs for your functions
void conf_Desk(){}
void callibration(){}
void waitPress()
{
if (buttonState == HIGH)
{
lastPressed = millis();
State = momentPress;
return;
}
else
return;
}
void momentPress()
{
if (buttonState == LOW)
{
snooze();
State = waitPress;
return;
}
if (pressed > 15000)
State = shortPress;
return;
return;
}
void shortPress()
{
if (buttonState == LOW)
{
conf_Desk();
return;
}
if (pressed > 30000)
State = longPress;
return;
return;
}
void longPress()
{
if (buttonState == LOW)
{
callibration();
return;
}
return;
}
void loop()
{
buttonState = digitalRead(buttonPin);
State();
}
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++;
}
}
}
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.
I'm using the AccelStepper library to control my stepper motor, and I'm having difficulty figuring out how to get my motor to stop when my button is pushed.
I can get the motor to stop once it completes its entire movement from the moveTo command, but I can't get it to stop before it finishes. I've tried using an if statement nested within the while loop I'm using to get the motor to run, but no dice.
The code as it stands is as follows:
#include <AccelStepper.h>
const int buttonPin=4; //number of the pushbutton pin
int buttonState=0;
int motorSpeed = 9600; //maximum steps per second (about 3rps / at 16 microsteps)
int motorAccel = 80000; //steps/second/second to accelerate
int motorDirPin = 8; //digital pin 8
int motorStepPin = 9; //digital pin 9
int state = 0;
int currentPosition=0;
//set up the accelStepper intance
//the "1" tells it we are using a driver
AccelStepper stepper(1, motorStepPin, motorDirPin);
void setup(){
pinMode(buttonPin,INPUT);
stepper.setMaxSpeed(motorSpeed);
stepper.setSpeed(motorSpeed);
stepper.setAcceleration(motorAccel);
stepper.moveTo(-12000); //move 2000 steps (gets close to the top)
}
void loop(){
while( stepper.currentPosition()!=-10000)
stepper.run();
// move to next state
buttonState = digitalRead(buttonPin);
currentPosition=stepper.currentPosition();
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
//if stepper is at desired location
if (buttonState == HIGH ){//need to find a way to alter current move to command
stepper.stop();
stepper.runToPosition();
stepper.moveTo(12000);
}
if(stepper.distanceToGo() == 0)
state=1;
if(state==1){
stepper.stop();
stepper.runToPosition();
stepper.moveTo(12000);
}
//these must be called as often as possible to ensure smooth operation
//any delay will cause jerky motion
stepper.run();
}
I don't know if you've figured it out already, but I stumbled upon this thread and noticed something which might or might not solve your problem. I'm working with accelstepper too at the moment.
I'm having the feeling that even though you use .stop to stop the motor, you're still assigning a new destination (stepper.moveTo(12000)), after which you still run the stepper.run() command at the bottom, causing the stepper to 'run towards 12000 steps'. Maybe try this?
uint8_t button_state = 0; // cf problem 1.
void loop() {
if (digitalRead(buttonPin) == HIGH) {
if (button_state == 0) {
stepper.stop();
stepper.moveTo(12000);
button_state = 1;
}
} else {
button_state = 0;
}
if (stepper.distanceToGo() == 0) {
stepper.stop();
stepper.moveTo(12000);
}
if(button_state = 0) {
stepper.run();
}
}
I don't know if this is going to work, but this way, if the button is pressed, the button_state variable should prevent the stepper from running when it's set on 1.
Hope this helps,
Just a passing by student
I see three problems in your code:
when you push the button, its state will be set to HIGH for the whole time you're pressing it, and it can be several loops. You'd better use a state variable that triggers what you want to do on button press only once.
looking at the documentation, you're using stepper.runToPosition(), which blocks until it reaches the destination. So the more you click, the more it could get blocked. You'd better use only the stepper.moveTo() and stepper.run() method that enables to use a few cycles for interactions.
at the beginning of your loop, you make your code block until stepper.currentPosition gets to -10000. So you surely are blocking until it gets there, removing all reactivity on every loop() iteration.
you may better work your loop() function out as follows:
uint8_t button_state = 0; // cf problem 1.
void loop() {
if (digitalRead(buttonPin) == HIGH) {
if (button_state == 0) {
stepper.stop();
stepper.moveTo(12000);
button_state = 1;
}
} else {
button_state = 0;
}
if (stepper.distanceToGo() == 0) {
stepper.stop();
stepper.moveTo(12000);
}
stepper.run();
}