Hi there.
I have switches controlling relays (onebutton library OneButton - Arduino Reference ) and when client is connected I can control relays both with MQTT (PubSubClient - Arduino Reference) and switches.
When there is no connection I cannot do anything as delay in reconnect() blocks execution of other code.
void loop() {
// keep watching the push buttons:
button1.tick();
button2.tick();
button3.tick();
button4.tick();
button5.tick();
button6.tick();
button7.tick();
button8.tick();
button9.tick();
button10.tick();
button11.tick();
button12.tick();
button13.tick();
button14.tick();
button15.tick();
button16.tick();
button17.tick();
button18.tick();
button19.tick();
button20.tick();
button21.tick();
button22.tick();
button23.tick();
button24.tick();
button25.tick();
button26.tick();
button27.tick();
button28.tick();
button29.tick();
button30.tick();
button31.tick();
button32.tick();
button33.tick();
button34.tick();
button35.tick();
button36.tick();
// You can implement other code in here or just wait a while
delay(10);
if (!client.connected()) {
reconnect();
}
client.loop();
} // end of loop
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266Client")) {
Serial.println("connected");
// Subscribe
client.subscribe(sub1);
client.subscribe(sub2);
client.subscribe(sub3);
client.subscribe(sub4);
client.subscribe(sub5);
client.subscribe(sub6);
client.subscribe(sub7);
client.subscribe(sub8);
client.subscribe(sub9);
client.subscribe(sub10);
client.subscribe(sub11);
client.subscribe(sub12);
client.subscribe(sub13);
client.subscribe(sub14);
client.subscribe(sub15);
client.subscribe(sub16);
client.subscribe(refresh);
// Publish states
publishAll();
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
I tried to use millis() but could not make it to work.
How to allow other code to be executed when trying to reconnect?
At the moment when there is no ethernet connection the loop is stuck in void reconnect() because it tries to reconnect.
If it fails to reconnect the delay(5000) is executed and I cannot read button presses.
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
unsigned long myTime = 0;
unsigned long rememberedTime = 0;
unsigned long difference = 0;
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
myTime = millis();
difference = myTime - rememberedTime;
if (difference >= 5000UL) {
rememberedTime = myTime;
Serial.println(myTime);
// Should I put reconnect() here???
// I don't know what to do next
}
}
You have made reconnect() not wait until a connection is successful
void loop() {
// keep watching the push buttons:
button1.tick();
button2.tick();
button3.tick();
button4.tick();
button5.tick();
button6.tick();
button7.tick();
button8.tick();
button9.tick();
button10.tick();
button11.tick();
button12.tick();
button13.tick();
button14.tick();
button15.tick();
button16.tick();
button17.tick();
button18.tick();
button19.tick();
button20.tick();
button21.tick();
button22.tick();
button23.tick();
button24.tick();
button25.tick();
button26.tick();
button27.tick();
button28.tick();
button29.tick();
button30.tick();
button31.tick();
button32.tick();
button33.tick();
button34.tick();
button35.tick();
button36.tick();
// You can implement other code in here or just wait a while
delay(10);
if (!client.connected()) {
reconnect();
}
client.loop();
} // end of loop
void reconnect() {
static unsigned long lastTime;
const unsigned long reconnectInterval = 5000UL;
if (!client.connected() && millis() - lastTime >= reconnectInterval) {
lastTime = millis();
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266Client")) {
Serial.println("connected");
// Subscribe
client.subscribe(sub1);
client.subscribe(sub2);
client.subscribe(sub3);
client.subscribe(sub4);
client.subscribe(sub5);
client.subscribe(sub6);
client.subscribe(sub7);
client.subscribe(sub8);
client.subscribe(sub9);
client.subscribe(sub10);
client.subscribe(sub11);
client.subscribe(sub12);
client.subscribe(sub13);
client.subscribe(sub14);
client.subscribe(sub15);
client.subscribe(sub16);
client.subscribe(refresh);
// Publish states
publishAll();
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
}
}
}
Your loop() code may need to adjust as well since you may not be connected after the call the reconnect(). It may be a if/else clause. This allows loop() to cycle quickly and react to your buttons.
FYI... You should use arrays for those buttons....
When you use delay() and process every button every time that void loop() runs, you don't learn how fast non-blocked AVR code runs... many 10's of times per millisecond.
Make the buttons an array ( button[ 36 ] ) and make ( byte buttonIndex; ) to process only 1 button per pass through void loop(). They will all be done is less than 1ms. This will shorten the code and make void loop() run more often, becoming smoother and more responsive.
I would suggest making reconnect into a task with its own timer.
First it checks if the connection is down, if not then return.
Next it checks millis() - lastTry >= 5000L, if not then return.
lastTry = millis()
Then the connect code you have minus the delay now as a millis time check in step 2.
Everything done through the connection should check the connection and be able to execute later if not connected "now".
Hello gggg1981gggg
Either run a button tutorial to gain the knowledge how the libary works or brew a button manager by yourself.
Have a nice day and enjoy coding in C++.
I looked into oneButton.h to see if it has a constructor that can make arrays.. it doesn't but could be added since there is an init function.
I have a new-to-me way to debounce a button. I keep a history byte of the last 8 pin reads done every 500 micros. Every new read is added after the old bits are left shifted once. --- and that's all the handler does is know the pin number and update history and trigger on timer.
Functions/tasks in loop() can all see that INPUT_PULLUP pin history.
01111111 == 127 is pin down and 7 reads (stability) of pin up, call it Release.
10000000 == 128 is pin up and 7 pin down (stability), call it Press.
0 is Down
255 is Up
Everything else is Bounce. That simple. Most sketches only need to look for 128 (Press).
But this made me think how to tighten bounce detection so ver. 2 is OTW.
It will cost some cycles but tighten bounce detection.
Some things you should know when using multiple digital inputs:
The pins are arranged as 8 per port.
AVR ports have 3 registers; the PIN register, the DDR-egister and the PORT register.
The DDR = Data Direction Register sets whether each pin is Input or Output.
The PORT register sets whether each pin is Low or High.
The PIN register is to read all the pin states at once has a superpower. If you write a byte with any HIGH bits, the corresponding pins will be toggled in the PORT register.
On an Uno, some pins already have a purpose but there are 3 Ports with 6 consecutive pins open meaning that up to 6 buttons can be read in 1 cycle and the used pins bit-masked off in the next cycle. You do this with Arduino search on direct port manipulation.
It is difficult to say which is more efficient code in terms of how many cycles it takes to poll and debounce the pin readings, but my preferred debounce code uses a millis() "start"variable and a "state" flag.
This code is for multiple buttons - as many as you might reasonably need. However, a variant could debounce 8 (or 16 ...) at a time such as for a keyboard.
char bstate1 = 0;
char bstate2 = 0;
char bstate3 = 0;
char bstate4 = 0;
unsigned long bcount1 = 0; // button debounce timer. Replicate as necessary.
unsigned long bcount2 = 0;
unsigned long bcount3 = 0;
unsigned long bcount4 = 0;
// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check
// Routines by Paul__B of Arduino Forum
// General timeout routine as used in butndown()
boolean timeout(unsigned long *marker, unsigned long interval) {
if (millis() - *marker >= interval) {
*marker += interval; // move on ready for next interval
return true;
}
else return false;
}
// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
// Routines by Paul__B of Arduino Forum
// This routine is passed the button read value and pointers (&bstate) to the timer and state variables for the particular button, returns a debounced Boolean for success.
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
switch (*butnstate) { // Odd states if was pressed, >= 2 if debounce in progress
case 0: // Button up so far,
if (button == HIGH) return false; // Nothing happening!
else {
*butnstate = 2; // record that is now pressed
*marker = millis(); // note when was pressed
return false; // and move on
}
case 1: // Button down so far,
if (button == LOW) return false; // Nothing happening!
else {
*butnstate = 3; // record that is now released
*marker = millis(); // note when was released
return false; // and move on
}
case 2: // Button was up, now down.
if (button == HIGH) {
*butnstate = 0; // no, not debounced; revert the state
return false; // False alarm!
}
else {
if (millis() - *marker >= interval) {
*butnstate = 1; // jackpot! update the state
return true; // because we have the desired event!
}
else
return false; // not done yet; just move on
}
case 3: // Button was down, now up.
if (button == LOW) {
*butnstate = 1; // no, not debounced; revert the state
return false; // False alarm!
}
else {
if (millis() - *marker >= interval) {
*butnstate = 0; // Debounced; update the state
return false; // but it is not the event we want
}
else
return false; // not done yet; just move on
}
default: // Error; recover anyway
{
*butnstate = 0;
return false; // Definitely false!
}
}
}
The point is that this code is not called at or only at certain times, but every time you pass through the loop, allowing that other things may delay the loop to widely varying degrees. So it does not police any particular number of polls of the button, but however many polls happen to occur within the specified interval of milliseconds (generally about 5 to 10).
I have a debouncer that checks state every time and looks for a given stable period to flag a stable transition.
And then I worked out that I didn't need all of that. Bounce happens over about 2ms but longer with dirty buttons, it always ends with a final transition to a new stable state. When history is 0, 127, 128 or 255, there's a stable state.
What I do times the reads far enough apart to use 1 byte history to measure a stable state longer than a bounce. Reading every 1 or 2 ms will also work but will take longer and still be "instant" in human terms. Also I time with 16 bit values instead of 32.
If you do use the millis clock, the accuracy is +/- 1ms. That's not good for short intervals. When it matters, use micros() -- as Nick Gammon posted 10 years ago.