Hello,
I have a switch which uses a, ESP8266 and relay module connected to it.
It operates in two modes based on the selection made on a web page.
In mode 1 (Auto Mode), it turnes on and off based on the value from a sensor which my sensor module uploads to the website.
In mode 2 (Timer Mode), it operates based on timer value and duration entered in the website.
All the values entered in the website is stored in SQL database and are fetched from the database and compared.
So the issue I am facing is as follows.
Everything works fine in both the modes. But when I am in Timer Mode and I switch to Auto Mode, it waits till the timer duration is complete, before switching to Auto Mode. Is there any way to modify the program in such a way that it monitors the switch state all the time and changes mode accordingly even if the timer is running?
Relevant part of the code is as below
int ontime = 5000;
void loop() {
time_now = millis();
if (timermode == 0 && setvalue > sensorvalue) {
digitalWrite(device1_pin, HIGH);
Serial.println("Set Value is more than Actual Value. Switch Activated");
while(millis() < time_now + ontime){
yield();
}
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Auto Mode");
} else if (timermode == 0 && setvalue < sensorvalue) {
digitalWrite(device1_pin, LOW);
Serial.println("Set Value is less than Actual Value. Switch is OFF");
}
if (timermode == 1) {
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is ON in Timer Mode");
while(millis() < time_now + (duration*1000)){
yield();
}
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Timer Mode");
while(millis() < time_now + (timer*1000)){
yield();
}
}
}
Thanks a lot for the quick response.. sorry for not posting the code properly. I have rectified that..
I am trying to understand the "Blink Without Delay" example. I am a newbie and may have to trouble you again if I don't get anywhere.
Sorry about that
No need to say sorry. Really. You can ask as many questions as you like. If the readers can see that you have done at least a single step forward through own effort they will anser again and again.
The while loops counteract on what is called non-blocking timing
Me personal I think it is very important to give an easy to understand basic analogon how millis() is used.
This is what most millis() tutorials are missing. So here it is:
as an allday example with easy to follow numbers
delay() is blocking. As long as the delay is "delaying" nothing else of the code can be executed.
Now there is a technique of non-blocking timing.
The basic principle of non-blocking timing is fundamental different from using delay()
You have to understand the difference first and then look into the code.
otherwise you might try to "see" a "delay-analog-thing" in the millis()-code which it really isn't
Trying to see a "delay-analog-thing" in millis() makes it hard to understand millis()
Having understood the basic principle of non-blocking timing based on millis() makes it easy to understand.
imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in.
Baking time 10 minutes
You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02 (snapshot of time)
You start reading the newspaper and from time to time looking onto your watch
watch 13:02 not yet time
watch 13:03 not yet time
watch 13:04 not yet time 13:04 - 13:02 = 2 minutes is less than 3 minutes
watch 13:05 when did I start 13:02? OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven
New basetime 13:05 (the snapshot of time)
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time (13:14 - 13:05 = 9 minutes is less than 10 minutes
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)
You did a repeated comparing how much time has passed by
This is what non-blocking timing does
In the code looking at "How much time has passed by" is done
currentTime - startTime >= bakingTime
bakingTime is 10 minutes
13:06 - 13:05 = 1 minute >= bakingTime is false
13:07 - 13:05 = 2 minutes >= bakingTime is false
...
13:14 - 13:05 = 9 minutes >= bakingTime is false
13:15 - 13:05 = 10 minutes >= bakingTime is TRUE time for timed action!!
if (currentTime - previousTime >= period) {
it has to be coded exactly this way because in this way it manages the rollover from Max back to zero
of the function millis() automatically
baldengineer.com has a very good tutorial about timing with function millis() too .
There is one paragraph that nails down the difference between function delay() and millis() down to the point:
The millis() function is one of the most powerful functions of the Arduino library. This function returns the number of milliseconds the current sketch has been running since the last reset. At first, you might be thinking, well that’s not every useful! But consider how you tell time during the day. Effectively, you look at how many minutes have elapsed since midnight. That’s the idea behind millis()!
Instead of “waiting a certain amount of time” like you do with delay(), you can use millis() to ask “how much time has passed”?
I relation to your code
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is ON in Timer Mode");
if (enough time has passed by)
digitalWrite(device1_pin, LOW);
Serial.println("Switch is ON in Timer Mode");
That was a very detailed explanation and I'm glad that there are people out there to help..
The baldengineer.com website really helped an I am getting a hang of what non-blocking code is all about..
Thanks alot for the detailed explanation..
I edited the code and the AUTO mode is kind of working as expected. I changed it as below
if (timermode == 0 && setvalue > sensorvalue) {
digitalWrite(device1_pin, HIGH);
Serial.println("Set Value is more than Actual Value. Switch Activated");
if ((unsigned long)(currentMillis - previousMillisDuration) >= duration) {
Switchstate = !Switchstate;
digitalWrite(device1_pin, Switchstate);
previousMillisDuration = currentMillis;
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Auto Mode");
}
} else if (timermode == 0 && setvalue < sensorvalue) {
digitalWrite(device1_pin, LOW);
Serial.println("Set Value is less than Actual Value. Switch is OFF");
}
When I switch to TIMER mode, the switch turns on, stays ON for the set duration and turns OFF, which is good.
But it is expected to remain OFF for the "timer" duration, and then turn back on, remain ON for the set "duration" and then turn OFF. This cycle is not happening. The code I wrote is as below
if (timermode == 1) {
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is ON in Timer Mode");
if ((unsigned long)(currentMillis - previousMillisDuration) >= duration) {
Switchstate = !Switchstate;
digitalWrite(device1_pin, Switchstate);
previousMillisDuration = currentMillis;
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Timer Mode");
}
if ((unsigned long)(currentMillis - previousMillisTimer) >= timer) {
previousMillisTimer = currentMillis;
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is OFF after Set duration in Timer Mode");
}
}
You should always post the complete sketch. From the snippet I can't see how you defined the variables
Also a typecast to unsigned long looks odd to me. Maybe it is working never tried it that way
Just define all variables that are related to millis() as unsigned long and then your if-conditions look like this
if ((currentMillis - previousMillisDuration) >= duration)
The variablename previousMillisDuration is confusing it is not a duration it is the previous timestamp
for programming different durations you could assign different values to a duration-variable and then check always with the same if-condition.
unsigned long previousMillisDuration=0;
unsigned long previousMillisTimer=0;
boolean Switchstate = false;
void setup () {
Serial.begin(115200);
pinMode(device1_pin, OUTPUT);
}
void loop() {
unsigned long currentMillis = millis();
if (timermode == 0 && setvalue > sensorvalue) {
digitalWrite(device1_pin, HIGH);
Serial.println("Set Value is more than Actual Value. Switch Activated");
if ((unsigned long)(currentMillis - previousMillisDuration) >= duration) {
Switchstate = !Switchstate;
digitalWrite(device1_pin, Switchstate);
previousMillisDuration = currentMillis;
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Auto Mode");
}
} else if (timermode == 0 && setvalue < sensorvalue) {
digitalWrite(device1_pin, LOW);
Serial.println("Set Value is less than Actual Value. Switch is OFF");
}
if (timermode == 1) {
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is ON in Timer Mode");
if ((unsigned long)(currentMillis - previousMillisDuration) >= duration) {
Switchstate = !Switchstate;
digitalWrite(device1_pin, Switchstate);
previousMillisDuration = currentMillis;
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Timer Mode");
}
if ((unsigned long)(currentMillis - previousMillisTimer) >= timer) {
previousMillisTimer = currentMillis;
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is OFF after Set duration in Timer Mode");
}
}
I prefer not to use the cast. The cast is confusing and gives the impression that something needs to be fixed. If something needs to be fixed, then the millis-timer does not work.
if ((unsigned long)(currentMillis - previousMillisDuration) >= duration) // with cast for no reason
if (currentMillis - previousMillisDuration >= duration) // okay, this is what I use
if ((currentMillis - previousMillisDuration) >= duration) // okay
Can you take some time to go through the next lines ? It is like going back to school
Could you read my post in an other topic: Continuous serial communication between arduino and pc - #4 by Koepel and click on that video what Andreas Spiess thinks about it.
Andreas Spiess is referring to non-blocking code with millis() and he mentioned someone who uses also a Finite State Machine with that. He calls it: wearing a shirt inside out.
Your sketch is controlled by the variable 'timermode'. So the sketch can be in a few different states depending on that variable. You could call that variable 'state'.
I re-read your description but I'm unsure if understand it right.
It would be very good if you post an example with easy to calculate numbers.
at the moment I understand your description this way
no timing at all just switching on/off depends only on the sensor-value not on any times.
But your code has timing for
if (timermode == 0 && setvalue > sensorvalue) {
So please give examples for each case
automode is activated
20:00:00 sensor-value goes below threshold what should happen
time goes on
20:00:01
20:00:02
...
what shall happen?
next case
sensorvlaue goes below threshold what shall hapen?
timer-mode gets activated
21:00:00
21:00:01
21:00:02
21:00:03 which condition becomes true? What shall happen?
21:00:04
21:00:05
....
which condition becomes true? What shall happen ?
"timermode" is actually the state of the selector switch in the webpage which selects between "AUTO" and "TIMER" modes. When it is "0" it is in "AUTO" mode and when it is "1" it is in "TIMER" mode.. I am posting the full code below. Hope that will give full understanding of what I am trying to achieve..
Please note that some parts of the code may be irrelevant as I am still testing.. also removed the website address as I am not allowed to share that..
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
static const uint8_t D0 = 16;
static const uint8_t D1 = 5;
static const uint8_t D2 = 4;
static const uint8_t D3 = 0;
static const uint8_t D4 = 2;
static const uint8_t D5 = 14;
static const uint8_t D6 = 12;
static const uint8_t D7 = 13;
static const uint8_t D8 = 15;
static const uint8_t D9 = 3;
static const uint8_t D10 = 1;
int device1_pin = D5;
int device2_pin = D6;
int device3_pin = D7;
int device4_pin = D8;
int sensorvalue = 0;
int setvalue = 0;
int timer = 0;
int duration = 0;
int timermode = 0;
int ontime = 5000;
unsigned long previousMillisDuration=0;
unsigned long previousMillisTimer=0;
boolean Switchstate = false;
void setup () {
Serial.begin(115200);
WiFiManager wifiManager;
wifiManager.autoConnect("Device");
Serial.println("Connected to WiFi");
pinMode(device1_pin, OUTPUT);
pinMode(device2_pin, OUTPUT);
pinMode(device3_pin, OUTPUT);
pinMode(device4_pin, OUTPUT);
}
void loop() {
unsigned long currentMillis = millis();
if (timermode == 0 && setvalue > sensorvalue) {
digitalWrite(device1_pin, HIGH);
Serial.println("Set Value is more than Actual Value. Switch Activated");
if ((unsigned long)(currentMillis - previousMillisDuration) >= duration) {
Switchstate = !Switchstate;
digitalWrite(device1_pin, Switchstate);
previousMillisDuration = currentMillis;
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Auto Mode");
}
} else if (timermode == 0 && setvalue < sensorvalue) {
digitalWrite(device1_pin, LOW);
Serial.println("Set Value is less than Actual Value. Switch is OFF");
}
if (timermode == 1) {
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is ON in Timer Mode");
if ((unsigned long)(currentMillis - previousMillisDuration) >= duration) {
Switchstate = !Switchstate;
digitalWrite(device1_pin, Switchstate);
previousMillisDuration = currentMillis;
digitalWrite(device1_pin, LOW);
Serial.println("Switch is OFF after Set duration in Timer Mode");
}
if ((unsigned long)(currentMillis - previousMillisTimer) >= timer) {
Switchstate = !Switchstate;
digitalWrite(device1_pin, Switchstate);
previousMillisTimer = currentMillis;
digitalWrite(device1_pin, HIGH);
Serial.println("Switch is OFF after Set duration in Timer Mode");
}
}
//delay(2000); //Send a request every 30 seconds
}
OK, let me try to put it as an example with numbers
Condition 1: Mode switch in the website in AUTO (timermode = 0)
There are sensorvalue, setvalue and duration. Lets say duration is set to 5 seconds
If setvalue is greater than sensorvalue, the switch is activated (if (timermode == 0 && setvalue > sensorvalue) {
digitalWrite(device1_pin, HIGH);
20:00:00
20:00:01
20:00:02
20:00:03
20:00:04
20:00:05 Switch turns off
Again goes back to see if the mode is still in AUTO and compare sensorvalue and setvalue. If the condition is same, the cycle repeats till sensorvalue is more than setvalue.
As long as the sensorvalue is more than setvalue, the switch remains off
Condition 2: Mode switch in TIMER mode (timermode = 1)
There is Timer and Duration. Lets say timer is set to 60 seconds and duration to 5 seconds
The switch is activated. (if (timermode == 1) {
digitalWrite(device1_pin, HIGH);
20:00:00 Switch turns on upon selecting TIMER mode
20:00:01
20:00:02
20:00:03
20:00:04
20:00:05 Switch turns off (after duration of 5 seconds)
20:00:06 Switch remains off
20:00:07
20:00:08
20:00:09
20:00:10 ........
.....
20:01:06 Switch turns on (after timer of 60 seconds)
20:01:07
20:01:08
20:01:09
20:01:10
20:01:11 Switch turns off (after duration of 5 seconds)
This cycle repeats as long as the mode switch is in TIMER mode. At any point of time, if the timer value or duration in changed in the website, the timing changes accordingly. At all times, the MODE switch state is monitored and depending on whether it is in AUTO or TIMER, the operation changes to Condition 1 or Condition2 above.
Hope this time I explained as you expected.
Thanks!!
Thanks a lot for the links and for sharing the ino file..
I tried reading, though most of it went over my head, it really took me to several new openings.. I never knew Arduino could do this much..
I need to understand more of basics in order to fully understand the concept I guess, and I am really into that now.. appreciate your support..
inside code that gets executed with variable myState == WaitForTimerONTime_End
if (currentMillis - TimerModeOnTimeStamp > TimerModeONPeriod)
myState = Timer_Start_OFF_Period
inside code that gets executed with variable myState == Timer_Start_OFF_Period
OFFPeriodStartsTimestamp = currentMillis
myState = WaitForTimerOFF_PeriodToEnd
inside code that gets executed with variable myState == WaitForTimerOFF_PeriodToEnd
if (currentMillis - OFFPeriodStartsTimestamp > OFF_Period)
myState = CheckMode
So this statemachine enable the two possible loops
looping through automode
or looping through timermode
where inside state CheckMode the decision which loop shall be started is made.
Thanks for the detailed explanation..
I kind of understand the concept, but I really can't figure out how exactly to edit my code to change it to statemachine with non-blocking timing.
I am still trying to understand by also reading the links and code sent by Koepel.
Would appreciate if you could make it a bit more simpler for me to understand how update my code.
the state-machine needs a variable that gets assigned different values that represent the different "states" what would be a good name for you that is kind of a generalised word for the different states.
With generalised word I mean this:
there are cars, scooters, trucks, buses, bicycles etc. etc., etc.they are all
generalised word "vehicles"
You have states
"CheckMode"
"SwitchOn"
"WaitForSwitchOnTimeBeeingOver"
etc. etc.
what would for you be a name that is "generalised"
The basic structure is a switch case-structure
here is a rough sketchy sketching of it
const byte CheckMode = 10;
const byte CheckValue = 15;
const byte AutoModeSwitchOn = 20;
const ....... = 30;
....
byte MyStateVariable;
switch(MyStateVariable)
{
case CheckMode:
// code
if (mode == Auto)
MyStateVariable = CheckValue;
break;
case CheckValue:
if (setvalue ....
MyStateVariable = AutoModeSwitchOn;
break;
case AutoModeSwitchOn:
// code
break;
No it is up to you to make a first attempt of adapting and expanding this sketchy sketching towardsa a working code and then come back with questions.
the additional word const tells the compiler this symbol is not variable it is a constant.
and you try to assign a constant a new value. That is what the compiler is complaining about.
all variables that deal in some way with millis() must be of type unsigned long.
millis() counts up until 2^32 = 4.294.967.296
such a huge number needs four bytes to be represented in the binary system.
a variable of type byte has 8 bit which menas can hold values from 0 to 255
int has 16 bit and can hold values from -32767..0...+32767
one bit is used as the sign
unsigned int holds values between 0 .. 65535
using symbols for the state makes the code much easier to read and understand
case 7:
says nothing about this state
case SwitchOnPump:
is explaining itself with the symbols name
maybe you should do a step inbetween
and make a handdrawn program schedule
something like this
the list should show what happends after each other and what where the conditions that something new shall happen.
There are two different types
something shall be done and immidiately after this is done
for example switching something on ad starting a timer
and some kind of condition must become true before something new should be done
examples:
wait until 5 seconds has passed by
check if sensorvalue is below setvalue
if you have written down all these conditions you can see how many states your state-machine needs and what are the types of the state transition or condition
and what happends in each state and what state follows
It will be much better if you take time to think about selfexplaining names that nail down to the point what a state is good for.
Sometimes this takes me 15 minutes to "develop" a goodname. But after that time
the name is really good self-explaining the thing.
Hello,
I edited the code again and completed the steps. It is compiling without any errors. After uploading, when I run it, nothing is happening. The serial monitor doesn't show any messages in the code
Below is what I made.
switch(MyStateVariable)
{
case CheckMode:
// code
if (timer == 0){
MyStateVariable = CheckValue;
}
if (timer == 1){
MyStateVariable = OnOffTiming;
}
break;
case CheckValue:
if (setvalue > actualvalue){
MyStateVariable = AutoModeSwitchOn;
break;
}
case AutoModeSwitchOn:
digitalWrite(device1_pin, HIGH);
Serial.println("Actual Moisture Less Than Set Moisture. Switch Activated");
SwitchOnTimeStamp = currentMillis;
MyStateVariable = WaitForAutoModeOnTimeToEnd;
break;
case WaitForAutoModeOnTimeToEnd:
if (currentMillis - SwitchOnTimeStamp >= ontime){
Serial.println("Switch Turned Off After Set Duration in AUTO mode");
MyStateVariable = CheckMode;
break;
}
case OnOffTiming:
digitalWrite(device1_pin, HIGH);
TimerModeOnTimeStamp = currentMillis;
Serial.println("Switch ON in TIMER mode");
MyStateVariable = WaitForTimerONTime_End;
break;
case WaitForTimerONTime_End:
if (currentMillis - TimerModeOnTimeStamp > duration){
Serial.println("Switch Turned OFF in TIMER mode After Set Duration");
MyStateVariable = Timer_Start_OFF_Period;
break;
}
case Timer_Start_OFF_Period:
OFFPeriodStartsTimestamp = currentMillis;
MyStateVariable = WaitForTimerOFF_PeriodToEnd;
break;
case WaitForTimerOFF_PeriodToEnd:
if (currentMillis - OFFPeriodStartsTimestamp > timer){
Serial.println("Switch ON in TIMER mode After Set Timer Period");
MyStateVariable = CheckMode;
break;
}
}
}
Please see if I am doing something wrong..
Thanks!!
Pretty well done as a first attempt. Still some smaller things to correct.
Yes "of course" there is nothing happeing yet because the most basic structure that all sketches need is missing.
From this I can conclude that you should improve your knowledge about how writing programs with teh Arduino-IDE works. All any every sketch - without any exception
All any every sketch needs a void setup {} abd a void loop() {} function.
So as you seem to not know yet enough about these basic things I highly recommend to read at least the first 5 sections of this programming tutorial
It is easy to understand and has a good mixture between explaining important concepts and example-codes to get you going. So give it a try and report your opinion about this tutorial.
To rise your motivation to really do it I make a condition:
Any further answers from me depend on that you wrote an attempt to add the functions setup and loop to the code above and posted this new codeversion here.
As a general hint
There is an automatic function for doing this in the Arduino-IDE
just three steps
press Ctrl-T for autoformatting your code
do a rightclick with the mouse and choose "copy for forum"