Im trying to use a Sparkfun Pro Micro as a controller for my keyboard. I can't achieve a normal keyboard's behaviour. This is my code:
#include <Keyboard.h>
int firstButtonPin = 18, secondButtonPin = 19;
char firstButtonChar = 'z', secondButtonChar = 'x';
bool firstButtonPressed = false, secondButtonPressed = true;
void setup() {
pinMode(firstButtonPin, INPUT);
pinMode(secondButtonPin, INPUT);
Keyboard.begin();
}
void loop() {
if(digitalRead(firstButtonPin) == HIGH && firstButtonPressed == false) Keyboard.press(firstButtonChar), firstButtonPressed = true;
else if(digitalRead(firstButtonPin) != HIGH && firstButtonPressed) Keyboard.release(firstButtonChar), firstButtonPressed = false;
if(digitalRead(secondButtonPin) == HIGH && secondButtonPressed == false) Keyboard.press(secondButtonChar), secondButtonPressed = true;
else if(digitalRead(secondButtonPin) != HIGH && secondButtonPressed) Keyboard.release(secondButtonChar), secondButtonPressed = false;
}
YT link: https://youtu.be/VfHNOtq4HHo.
As you can see, normal keyboard outputs a single key and after some time it spams. When i switch AVR, i get spam all the time (it looks like the keys are not being holded, just pressed and released in a very short period of time. How can I achieve a real keyboard's behaviour? I'd like to use it in games like Osu!. I hope you can help me. Thanks in advance
The problem was button bouncing.
u can use millis() to handle debouncing.
uint32_t time = millis(); //must be 32 bit or greater
uint8_t debounceTime = 50 //this is in milli seconds
void loop() {
if (millis() - time > debounceTime) {
//put your if-else-if code here (for keypress and release)
time = millis();
}
}
you have play around with the debounceTime. for most buttons 50 milli-seconds will do. but if you are using keyboard switches (like cherry mx, gateron etc) you can go as low as 5 milli-seconds
Related
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;
}
}
I'm trying to make a sketch for Arduino that differentiates between single, double, triple, maybe quadruple clicks, and hold clicks with a push button.
I've tried making my own program that uses a variable that increments with each click but haven't had much success. I then found this Arduino sketch that mostly does what I need, but lacks triple and quadruple clicks.
https://forum.arduino.cc/index.php?topic=14479.0
I just know I need to copy the same logic the double click uses but haven't figured it out.
/* 4-Way Button: Click, Double-Click, Press+Hold, and Press+Long-Hold Test Sketch
By Jeff Saltzman
Oct. 13, 2009
To keep a physical interface as simple as possible, this sketch demonstrates generating four output events from a single push-button.
1) Click: rapid press and release
2) Double-Click: two clicks in quick succession
3) Press and Hold: holding the button down
4) Long Press and Hold: holding the button for a long time
*/
#define buttonPin 19 // analog input pin to use as a digital input
#define ledPin1 17 // digital output pin for LED 1
#define ledPin2 16 // digital output pin for LED 2
#define ledPin3 15 // digital output pin for LED 3
#define ledPin4 14 // digital output pin for LED 4
// LED variables
boolean ledVal1 = false; // state of LED 1
boolean ledVal2 = false; // state of LED 2
boolean ledVal3 = false; // state of LED 3
boolean ledVal4 = false; // state of LED 4
//=================================================
void setup() {
// Set button input pin
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH );
// Set LED output pins
pinMode(ledPin1, OUTPUT);
digitalWrite(ledPin1, ledVal1);
pinMode(ledPin2, OUTPUT);
digitalWrite(ledPin2, ledVal2);
pinMode(ledPin3, OUTPUT);
digitalWrite(ledPin3, ledVal3);
pinMode(ledPin4, OUTPUT);
digitalWrite(ledPin4, ledVal4);
}
void loop() {
// Get button event and act accordingly
int b = checkButton();
if (b == 1) clickEvent();
if (b == 2) doubleClickEvent();
if (b == 3) holdEvent();
if (b == 4) longHoldEvent();
}
//=================================================
// Events to trigger
void clickEvent() {
ledVal1 = !ledVal1;
digitalWrite(ledPin1, ledVal1);
}
void doubleClickEvent() {
ledVal2 = !ledVal2;
digitalWrite(ledPin2, ledVal2);
}
void holdEvent() {
ledVal3 = !ledVal3;
digitalWrite(ledPin3, ledVal3);
}
void longHoldEvent() {
ledVal4 = !ledVal4;
digitalWrite(ledPin4, ledVal4);
}
//=================================================
// MULTI-CLICK: One Button, Multiple Events
// Button timing variables
int debounce = 20; // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 250; // max ms between clicks for a double click event
int holdTime = 1000; // ms hold period: how long to wait for press+hold event
int longHoldTime = 3000; // ms long hold period: how long to wait for press+hold event
// Button variables
boolean buttonVal = HIGH; // value read from button
boolean buttonLast = HIGH; // buffered value of the button's previous state
boolean DCwaiting = false; // whether we're waiting for a double click (down)
boolean DConUp = false; // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true; // whether it's OK to do a single click
long downTime = -1; // time the button was pressed down
long upTime = -1; // time the button was released
boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false; // when held, whether to wait for the up event
boolean holdEventPast = false; // whether or not the hold event happened already
boolean longHoldEventPast = false;// whether or not the long hold event happened already
int checkButton() {
int event = 0;
buttonVal = digitalRead(buttonPin);
// Button pressed down
if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
{
downTime = millis();
ignoreUp = false;
waitForUp = false;
singleOK = true;
holdEventPast = false;
longHoldEventPast = false;
if ((millis()-upTime) < DCgap && DConUp == false && DCwaiting == true) DConUp = true;
else DConUp = false;
DCwaiting = false;
}
// Button released
else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce)
{
if (not ignoreUp)
{
upTime = millis();
if (DConUp == false) DCwaiting = true;
else
{
event = 2;
DConUp = false;
DCwaiting = false;
singleOK = false;
}
}
}
// Test for normal click event: DCgap expired
if ( buttonVal == HIGH && (millis()-upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
{
event = 1;
DCwaiting = false;
}
// Test for hold
if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
// Trigger "normal" hold
if (not holdEventPast)
{
event = 3;
waitForUp = true;
ignoreUp = true;
DConUp = false;
DCwaiting = false;
//downTime = millis();
holdEventPast = true;
}
// Trigger "long" hold
if ((millis() - downTime) >= longHoldTime)
{
if (not longHoldEventPast)
{
event = 4;
longHoldEventPast = true;
}
}
}
buttonLast = buttonVal;
return event;
}
If you want background, this is for a device for someone to navigate Windows who can only use one button and a joystick, and this is what my current idea requires, but I'm not a programmer so having a bit of a rough time.
I am currently building myself some lighting for my apartment using Arduinos and strips of WS2812b LEDs. My plan was to control them with an app from my smartphone and a Raspberry Pi. The phone sends data (like color, mode etc.) to the Pi and the Pi transmits that via a 433MHz transmitter to three Arduino Nanos I wanted to place across the room.
Sending data from my phone through the Pi to the Arduinos works without a problem, the data is received correctly and the LEDs light up, but as soon as I do this once no more data can be received. There is no issue when I just receive and don't do anything else.
Here's my code:
#include <RCSwitch.h>
#include <Adafruit_NeoPixel.h>
#define LEDPIN 9
#define NUMPIXELS 75
#define STARTBIT 4194305
#define STOPBIT 8388609
#define SETBIT 2097153
RCSwitch rc = RCSwitch();
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, LEDPIN, NEO_GRB + NEO_KHZ800);
uint32_t data[] = {0,0,0};
bool written[] = {false, false, false};
bool led_set = false;
uint32_t wait = 0;
uint16_t j = 0;
int index = 0;
bool recording = false;
int led = 0;
unsigned long time;
static unsigned long received = 0;
void setup() {
Serial.begin(9600);
time = millis();
pinMode(LEDPIN, INPUT);
rc.enableReceive(0); // Receiver on interrupt 0 => that is pin #2
strip.begin();
for (int i = 0; i < 75; i++) {
strip.setPixelColor(i, strip.Color(255,255,255));
}
strip.show();
}
void loop() {
if (millis()-time >= 1000) {
Serial.println(*((unsigned int*)rc.nReceivedValue));
Serial.println(rc.nReceivedValue);
Serial.println(millis());
time = millis();
}
if (rc.available()) {
received = rc.getReceivedValue();
rc.resetAvailable();
Serial.print("received: ");
Serial.print(received);
Serial.println();
}
if ((received == STOPBIT) && recording) {
Serial.println("stopped");
recording = false;
}
else if ((received == STARTBIT) && not recording) {
recording = true;
led_set = false;
Serial.println("started");
}
if (recording) {
if(received == SETBIT && written[index] && index < 3) {
index++;
Serial.println("setbit");
}
else if(received != STARTBIT && received != STOPBIT && received != SETBIT && received != 0 && not written[index] && index < 3) {
data[index] = received & 0xFFFFFFFF;
written[index] = true;
//Serial.println(received);
Serial.println(data[index]);
Serial.println("written");
}
}
else if (data[0] != 0 && data[1] != 0 && data[2] != 0 && not recording && not led_set) {
Serial.println("setting LED");
for (int i = 0; i < NUMPIXELS; i++) {
strip.setPixelColor(i, data[2]);
}
strip.show();
rc.resetAvailable();
}
if (not recording) {
for (int k = 0; k < 3; k++) {
written[k] = false;
}
index = 0;
}
}
(I pretty much ripped this code to pieces and this is what's left.)
These are the transmitter/receivers and I use RC-Switch and Adafruit NeoPixel as libraries, other LED-libraries (FastLED, light_ws2812) had the same error though.
What I found out so far is that it works with less than 5 LEDs and sometimes with more if I set the colors to low values. Then it's more or less chance if I get another receive or not. If I do not set the LEDs here
else if (data[0] != 0 && data[1] != 0 && data[2] != 0 && not recording && not led_set) {
Serial.println("setting LED");
for (int i = 0; i < NUMPIXELS; i++) {
strip.setPixelColor(i, data[2]);
}
strip.show();
I can receive more data, if I hardcode the values for the colors it works only for bright white (so every color to 255) or for color values below 14.
If I comment out the last four lines in setup() the problem can't even be solved by the restart button (unplugging helps though). Switching the receiver does not work either.
All three of the Arduinos I use have the same issue.
I use a 6A power supply (at least for the strip with 75 LEDs, the other two have 30 LEDs each with a 2.4A supply) so power should be no issue.
When I hooked up my Arduino Uno to the second data pin of the receiver it couldn't receive anything either, using the Uno with another receiver next to it was fine. Using the Uno as oscilloscope and displaying the voltage on the data pin I noticed a lot of noise after first transmitting. It looked like it increased with the color values. The noise was gone after I restarted the Arduino with the LED-setting in setup() and it doesn't occur if I don't do anything with the LEDs at all. There is a 5 MOhm pull-down resistor between data and GND on the receiver module. I don't actually know if this noise has anything to do with this, because receiving was sometimes fine even with noise.
I tried disabling the receive pin before setting the LEDs and enabling it afterwards but that didn't help. Enabling interrupts manually doesn't do anything either. I changed the rc-switch interrupt handling so it prints something every time an interrupt occurs. That worked even after receiving the first data, The Arduino somehow just doesn't recognize what's been sent.
I really would like to know if there's anything I could try to solve this, besides switching to WiFi-modules or actual wire.
I'm trying to create an motion controlled light that doesn't turn on unless it's dark.
I want the light to turn on and off slowly with pwm when motion is detected. Right now my code works and does almost everything I want it to.
The problem is that I'm using the delay function to keep the light on after it reaches full brightness. And because of this if motion is detected while the light is on it doesn't reset the timer. Once the delay timer runs out and motion is detected the brightness will go down and then back up.
I would like to have the motion detector reset the timer that keeps the led on but haven't been able to get it working.
I have looked at these:
http://playground.arduino.cc/Code/AvoidDelay
https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
But because of my somewhat limited understanding of arduino code I haven't been able to get it working. I would really appreciate any help.
SOLVED
The working code below:
const byte MotionPin = 4; // Motionsensor
int LightSensorPin = 2; // Lightsensor
const byte LedPin = 11; // LED
unsigned long MotionDetectedMillis; // When motion was detected
unsigned long ledTurnedOnAt; // when led was turned on
unsigned long turnOnDelay = 50; // wait to turn on LED
unsigned long turnOffDelay = 5000; // turn off LED after this time
bool ledReady = false; // Has motion been detected
bool ledState = false; // Led on or off
int LightSensorState = 0;
int fadeAmount = 5; // How much to fade in a loop
byte brightness = 0;
void setup() {
pinMode(MotionPin, INPUT);
pinMode(LightSensorPin, INPUT);
pinMode(LedPin, OUTPUT);
digitalWrite(LedPin, LOW);
}
void loop() {
// get the time at the start of this loop()
unsigned long currentMillis = millis();
if (digitalRead(MotionPin) == HIGH && digitalRead(LightSensorPin)== HIGH)
{
MotionDetectedMillis = millis();
ledReady = true;
}
if (ledReady)
{
if ((unsigned long)(currentMillis - MotionDetectedMillis) >= turnOnDelay)
{
while (brightness < 255)
{
brightness = brightness + fadeAmount;
analogWrite(LedPin, brightness);
delay(30);
}
// setup our next "state"
ledState = true;
// save when the LED turned on
ledTurnedOnAt = currentMillis;
ledReady = false;
}
}
// see if we are watching for the time to turn off LED
if (ledState)
{
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay)
{
while (brightness >0)
{
brightness = brightness - fadeAmount;
analogWrite(LedPin, brightness);
delay(30);
}
ledState = false;
}
}
}
what you want is a state machine; you have to keep track in the loop of which state you currently are and what is the condition to trigger next state; because your loop will execute an action only if something happen to trigger a new state, instead of using delay() you will use a 'waiting for timeout' state (remember to keep a way to know what is the next state).
If this confuses you, imagine each state as step in the instruction manual;
turn on led 1, wait x, turn on led 1 a bit more, wait y, etc..
And because you have no more delay, before or after manage the "normal flow" of the state, you can add some special case like "if input 2 is high and current state is X or Y then set current state to Z".
So, what you want could be easily archived with an interrupt, but with a simple project like this one, you can get the same result just by removing the delay(OnTime) function.
If you want more on the interrupts of the atmega328 which is the one the Arduino UNO uses, go here
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();
}