Trying to make a media keypad that also keeps the computer awake. I have the media part handled, but cannot seem to get the keep awake part working properly. It will count to the 10 seconds and do what it's supposed to, but the problem is when I press any of the triggers it will then decrease the wait period.
#include <Keyboard.h>
#include <KeyboardLayout.h>
/*
Copyright (c) 2014-2015 NicoHood
See the readme for credit to other people.
Consumer example
Press a button to play/pause music player
You may also use SingleConsumer to use a single report.
See HID Project documentation for more Consumer keys.
https://github.com/NicoHood/HID/wiki/Consumer-API
*/
#include "HID-Project.h"
const int pinButton1 = 2;
const int pinButton2 = 3;
const int pinButton3 = 4;
const int pinButton4 = 5;
const int pinButton5 = 6;
const int pinButton6 = 7;
int lastCountMillis = 0;
int AwakeKey = 10000; // 60000 = 1 minute 300000 = 5 minutes
void setup() {
pinMode(pinButton1, INPUT_PULLUP);
pinMode(pinButton2, INPUT_PULLUP);
pinMode(pinButton3, INPUT_PULLUP);
pinMode(pinButton4, INPUT_PULLUP);
pinMode(pinButton5, INPUT_PULLUP);
pinMode(pinButton6, INPUT_PULLUP);
// Sends a clean report to the host. This is important on any Arduino type.
Consumer.begin();
}
void loop() {
if (millis() - lastCountMillis >= AwakeKey) {
Keyboard.press(KEY_F); //Testing purposes. Uncomment
//Keyboard.press(KEY_F13);
Keyboard.releaseAll();
delay(300);
lastCountMillis = millis();
}
if (!digitalRead(pinButton1)) {
Consumer.write(MEDIA_PLAY_PAUSE);
lastCountMillis = millis();
delay(300);
}
if (!digitalRead(pinButton2)) {
Consumer.write(MEDIA_PREVIOUS);
lastCountMillis = millis();
delay(300);
}
if (!digitalRead(pinButton3)) {
Consumer.write(MEDIA_VOLUME_MUTE);
lastCountMillis = millis();
delay(300);
}
if (!digitalRead(pinButton4)) {
Consumer.write(MEDIA_NEXT);
lastCountMillis = millis();
delay(300);
}
if (!digitalRead(pinButton5)) {
Consumer.write(MEDIA_VOLUME_UP);
lastCountMillis = millis();
delay(300);
}
if (!digitalRead(pinButton6)) {
Consumer.write(MEDIA_VOLUME_DOWN);
lastCountMillis = millis();
delay(300);
}
}
Just some general advice - millis() and delay() design patterns are usually incompatible with one another. millis() logic usually expects that the loop() function is running very fast, with no hundreds of millisecond delays.
Can you apply that insight to your problem?
I can't offer more than that now, because your program has minimal internal documentation, and you didn't really explain how it works (or is supposed to work) in detail.
Why do you use your timer variables (lastCountMillis and AwakeKey) as INT?
Time is always positive, never negative (as an INT can be).
I think:
mills() returns an UINT value (always positive, time is always positive)
but you combine with INT values (negative and positive possible)
you do math with minus, so that minus a negative value (INT, now also minus) becomes positive, converted back to unsigned: positive minus negative becomes: positive + positive.
So, I think, your INT math which should be actually an UINT math results in very large values, so that your comparison with SIGNED AwakeKey hits always true (at least 50% of the time during hours).
I assume, you see that just your delay(300) becomes effective, you see for a very long time always that:
if (millis() - lastCountMillis >= AwakeKey)
is true most of the time, never mind on what millis() you are.
Think about how to use SIGNED and UNSIGNED variables, what can go wrong, e.g. when the values become negative but taken as positive, etc.
You have a "roll-over" to very large value due to the UINT is taken as INT (and a negation on minus operation).
Don't mix, SIGNED and UNSIGNED on math operations (if you are not sure what happens "internally").
Yes, and also millis() returns an unsigned 32 bit value and lastCountMillis is an int, only 16 bits on the AVR.
You can "get away with" 16 or even 8 bit values for an interval like 'stayAwake', but you can be bitten if you forget and try using it to hold a greater value later on.
Never mind if int is 16bit or 32bit. Mixing INT and UINT is the issue here (I think).
An UINT taken as INT gets very small: 0xFFFF is -1. And vice versa. -1 is 0xFFFF, so a very large value in UINT range. And it jumps: from 1 (0x0001) to -1 (0xFFFF) there is 0xFFFE as UINT difference - very large (and not 2)!
Also true!
if millis() returns a 32bit value but you use via int just 16bit values - it fails as well.
So, doing an IF:
if (millis() - lastCountMillis >= AwakeKey)
where result of millis() is 32bit but you substract a 16bit - YES, it will be (most of the time) much larger as the 16bit AwakeKey - "always" true. Substraction from a large 32bit a 16bit value (which will never be greater as 16bit) - results still in a very large 32bit value.
Bear in mind (addressed to OP):
types are expanded implicitly, behind the scene: if one variable is 32bit (or UINT), all the other are expanded to 32bit (or UINT). So, a 16bit value will never reach the "upper value" range, but comparing or doing math with 32bit value range - a huge difference, potentially an "always" true result.
Check the types of return values from a function, the "value ranges" you use. Never mix, except you know what are you doing.
Bear in mind the "hidden, behind the scene" type conversions going on.
By the way, some people do use a truncated millis, so that for example, uint16_t can be used for faster execution and less memory allocation. By masking or explicit type conversion. The operational difference is only a shorter maximum time interval (65 seconds).
So that, for example, most debounce timers would work with intervals less than the 256 ms. limit when using an unsigned char (byte). Thus saving quite a lot of overhead. Edit - forgot to add, uint8_t for that would be better in most cases.
"uuhhh", you know people optimizing memory foot print by shortening variables? Cool.
Sure, an uint32_t has doubled the size as an uint16_t in memory.
But speed wise: no difference!!:
on 32bit platform, where uint32_t is 323bit can be copied with single instruction as uint_16 would need.
So, it is NOT faster! It can be even slower!!!!!
If your processor handles 32bit as an entity, but you decide to do all as bytes (8bit) - you have to read the memory 4 times just to get all the 4 bytes (in order to combine again a 32bit value).
The best performance you get when you use the "native" size for a word, 32bit on a 32bit processor, 16bit on a 16bit processor.
Even on some processors the doubled size works well, e.g. 64bit on a 32bit processor, assuming also 32bit on a 16bit processor - because: it has also a "single cycle" instruction to read/write a "double word".
Going down in "resolution" (to a smaller type) has no speed improvement, for sure.
Instead:
it creates "digital noise" : now your values have less resolution, they are rounded, you lose information, you degrade the Signal Noise Ration (SNR): e.g. on audio samples: 32bit vs. 16bit - much less distance to the noise floor.
When it comes to "truncate" time:
when millis() returns a 32bit result, but you use just 16bit: you discard the lower bits of time value. So, the time difference remains the same (you will wait still 32,000 milliseconds, even you use just 32 as indication).
Just your latency will larger! You cannot act anymore on 31,500 - you will wait for 32,000 always. So, it makes the resolution (and response time) larger at the end (you cannot do fine tuning anymore).
BTW: 256 ms to debounce a mechanical switch sounds way too large for me: it is 1/4 of a second. What an old/odd switch do you use?
A range to debounce in range of 10..50ms sounds reasonable for me.
I can do morse code with 1/4 second intervals.
I think you misunderstand. Not the truncation of lower bits - I mean upper bits. The time units (milliseconds) remains the same. So none of the side effects you mention, after the comments about native word sizes, apply to this.
And, the native word size on the AVR, is 8 bits.
256 ms to debounce a mechanical switch sounds way too large for me: it is 1/4 of a second.
It's better to have enough room to accommodate almost any switch. Most switches will bounce from 1-20ms. So that would be the chosen interval. There is no obligation to use the maximum value.
Here is the blink sketch, changed to use unsigned int, tested:
/*
Blink without Delay
changed to use unsigned int time variables
Turns on and off a light emitting diode (LED) connected to a digital pin,
without using the delay() function. This means that other code can run at the
same time without being interrupted by the LED code.
The circuit:
- Use the onboard LED.
- Note: Most Arduinos have an on-board LED you can control. On the UNO, MEGA
and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN
is set to the correct LED pin independent of which board is used.
If you want to know what pin the on-board LED is connected to on your
Arduino model, check the Technical Specs of your board at:
https://www.arduino.cc/en/Main/Products
created 2005
by David A. Mellis
modified 8 Feb 2010
by Paul Stoffregen
modified 11 Nov 2013
by Scott Fitzgerald
modified 9 Jan 2017
by Arturo Guadalupi
This example code is in the public domain.
https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
*/
// constants won't change. Used here to set a pin number:
const int ledPin = LED_BUILTIN;// the number of the LED pin
// Variables will change:
int ledState = LOW; // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned int previousMillis = 0; // will store last time LED was updated
// constants won't change:
const int interval = 1000; // interval at which to blink (milliseconds)
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// here is where you'd put code that needs to be running all the time.
// check to see if it's time to blink the LED; that is, if the difference
// between the current time and last time you blinked the LED is bigger than
// the interval at which you want to blink the LED.
unsigned int currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
I saved (860-832) = 28 bytes of RAM doing it that way, vs. the standard example. I made no other changes.