Closed abhishek2184 closed 5 years ago
`/**
This test illustrates the use if yield methods and internal StatusRequest objects
THIS TEST HAS BEEN TESTED ON NODEMCU V.2 (ESP8266)
The WiFi initialization and NTP update is executed in parallel to blinking the onboard LED
and an external LED connected to D2 (GPIO04)
Try running with and without correct WiFi parameters to observe the difference in behaviour
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STATUS_REQUEST
#include <TaskScheduler.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
Scheduler ts;
// Callback methods prototypes
void connectInit();
void ledCallback();
bool ledOnEnable();
void ledOnDisable();
void ledOn();
void ledOff();
void ntpUpdateInit();
// Tasks
Task tConnect (TASK_SECOND, TASK_FOREVER, &connectInit, &ts, true);
Task tLED (TASK_IMMEDIATE, TASK_FOREVER, &ledCallback, &ts, false, &ledOnEnable, &ledOnDisable);
// Tasks running on events
Task tNtpUpdate (&ntpUpdateInit, &ts);
// Replace with WiFi parameters of your Access Point/Router:
const char *ssid = "myssid";
const char *pwd = "************";
long ledDelayOn, ledDelayOff;
#define LEDPIN 2 // Onboard LED pin - linked to WiFi
//#define LEDPIN2 D2 // External LED
#define CONNECT_TIMEOUT 30 // Seconds
#define CONNECT_OK 0 // Status of successful connection to WiFi
#define CONNECT_FAILED (-99) // Status of failed connection to WiFi
// NTP Related Definitions
#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
unsigned long epoch;
WiFiUDP udp; // A UDP instance to let us send and receive packets over UDP
#define LOCAL_NTP_PORT 2390 // Local UDP port for NTP update
void setup() {
Serial.begin(115200);
Serial.println(F("TaskScheduler test #14 - Yield and internal StatusRequests"));
Serial.println(F("=========================================================="));
Serial.println();
pinMode (LEDPIN, OUTPUT);
// pinMode (LEDPIN2, OUTPUT);
tNtpUpdate.waitFor( tConnect.getInternalStatusRequest() ); // NTP Task will start only after connection is made
}
void loop() {
ts.execute(); // Only Scheduler should be executed in the loop
}
/**
Initiate connection to the WiFi network
*/
void connectInit() {
Serial.print(millis());
Serial.println(F(": connectInit."));
Serial.println(F("WiFi parameters: "));
Serial.print(F("SSID: ")); Serial.println(ssid);
Serial.print(F("PWD : ")); Serial.println(pwd);
WiFi.mode(WIFI_STA);
WiFi.hostname("esp8266");
WiFi.begin(ssid, pwd);
yield();
ledDelayOn = TASK_SECOND / 2;
ledDelayOff = TASK_SECOND / 4;
tLED.enable();
tConnect.yield(&connectCheck); // This will pass control back to Scheduler and then continue with connection checking
}
/**
Periodically check if connected to WiFi
Re-request connection every 5 seconds
Stop trying after a timeout
*/
void connectCheck() {
Serial.print(millis());
Serial.println(F(": connectCheck."));
if (WiFi.status() == WL_CONNECTED) { // Connection established
Serial.print(millis());
Serial.print(F(": Connected to AP. Local ip: "));
Serial.println(WiFi.localIP());
tConnect.disable();
}
else {
if (tConnect.getRunCounter() % 5 == 0) { // re-request connection every 5 seconds
Serial.print(millis());
Serial.println(F(": Re-requesting connection to AP..."));
WiFi.disconnect(true);
yield(); // This is an esp8266 standard yield to allow linux wifi stack run
WiFi.hostname("esp8266");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pwd);
yield(); // This is an esp8266 standard yield to allow linux wifi stack run
}
if (tConnect.getRunCounter() == CONNECT_TIMEOUT) { // Connection Timeout
tConnect.getInternalStatusRequest()->signal(CONNECT_FAILED); // Signal unsuccessful completion
tConnect.disable();
Serial.print(millis());
Serial.println(F(": connectOnDisable."));
Serial.print(millis());
Serial.println(F(": Unable to connect to WiFi."));
ledDelayOn = TASK_SECOND / 16; // Blink LEDs quickly due to error
ledDelayOff = TASK_SECOND / 16;
tLED.enable();
}
}
}
/**
Initiate NTP update if connection was established
*/
void ntpUpdateInit() {
Serial.print(millis());
Serial.println(F(": ntpUpdateInit."));
if ( tConnect.getInternalStatusRequest()->getStatus() != CONNECT_OK ) { // Check status of the Connect Task
Serial.print(millis());
Serial.println(F(": cannot update NTP - not connected."));
return;
}
udp.begin(LOCAL_NTP_PORT);
if ( WiFi.hostByName(ntpServerName, timeServerIP) ) { //get a random server from the pool
Serial.print(millis());
Serial.print(F(": timeServerIP = "));
Serial.println(timeServerIP);
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
}
else {
Serial.print(millis());
Serial.println(F(": NTP server address lookup failed."));
tLED.disable();
udp.stop();
tNtpUpdate.disable();
return;
}
ledDelayOn = TASK_SECOND / 8;
ledDelayOff = TASK_SECOND / 8;
tLED.enable();
tNtpUpdate.set( TASK_SECOND, CONNECT_TIMEOUT, &ntpCheck );
tNtpUpdate.enableDelayed();
}
/**
* Check if NTP packet was received
* Re-request every 5 seconds
* Stop trying after a timeout
*/
void ntpCheck() {
Serial.print(millis());
Serial.println(F(": ntpCheck."));
if ( tNtpUpdate.getRunCounter() % 5 == 0) {
Serial.print(millis());
Serial.println(F(": Re-requesting NTP update..."));
udp.stop();
yield();
udp.begin(LOCAL_NTP_PORT);
sendNTPpacket(timeServerIP);
return;
}
if ( doNtpUpdateCheck()) {
Serial.print(millis());
Serial.println(F(": NTP Update successful"));
Serial.print(millis());
Serial.print(F(": Unix time = "));
Serial.println(epoch);
tLED.disable();
tNtpUpdate.disable();
udp.stop();
}
else {
if ( tNtpUpdate.isLastIteration() ) {
Serial.print(millis());
Serial.println(F(": NTP Update failed"));
tLED.disable();
udp.stop();
}
}
}
/**
* Send NTP packet to NTP server
*/
unsigned long sendNTPpacket(IPAddress & address)
{
Serial.print(millis());
Serial.println(F(": sendNTPpacket."));
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
udp.beginPacket(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
yield();
}
/**
* Check if a packet was recieved.
* Process NTP information if yes
*/
bool doNtpUpdateCheck() {
Serial.print(millis());
Serial.println(F(": doNtpUpdateCheck."));
yield();
int cb = udp.parsePacket();
if (cb) {
// We've received a packet, read the data from it
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
// now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
epoch = secsSince1900 - seventyYears;
return (epoch != 0);
}
return false;
}
/**
* Flip the LED state based on the current state
*/
bool ledState;
void ledCallback() {
if ( ledState ) ledOff();
else ledOn();
}
/**
* Make sure the LED starts lit
*/
bool ledOnEnable() {
ledOn();
return true;
}
/**
* Make sure LED ends dimmed
*/
void ledOnDisable() {
ledOff();
}
/**
* Turn LEDs on.
* Set appropriate delay.
* PLEASE NOTE: NodeMCU onbaord LED is active-low
*/
void ledOn() {
ledState = true;
digitalWrite(LEDPIN, LOW);
//digitalWrite(LEDPIN2, HIGH);
tLED.delay( ledDelayOn );
}
/**
* Turn LEDs off.
* Set appropriate delay.
* PLEASE NOTE: NodeMCU onbaord LED is active-low
*/
void ledOff() {
ledState = false;
digitalWrite(LEDPIN, HIGH);
// digitalWrite(LEDPIN2, LOW);
tLED.delay( ledDelayOff );
}
`
This is the final Code if that helps.
I suspect this is what is happening. When I played with ESP, my experience was if it does not connect within, say, 5 seconds to the AP it will not connect at all. Therefore I implemented every 5 seconds retry here.
It is quite possible that 5 seconds is not enough in your case. Could you please change this line:
if (tConnect.getRunCounter() % 5 == 0) { // re-request connection every 5 seconds
to
if (tConnect.getRunCounter() % 10 == 0) { // re-request connection every 10 seconds
giving the chip 10 seconds to connect between attempts? If 10 is too short, just keep increasing the number until you hit the overall timeout of 30 seconds here.
#define CONNECT_TIMEOUT 30 // Seconds
Same thing for the NTP call here.
if ( tNtpUpdate.getRunCounter() % 5 == 0) {
Same 5 seconds. I should have parameterized it of course...
Hope this resolves the issue. Please let me know if it helped.
Hi,
Thanks for the suggestion, but even this is not working.
I've modified re-request interval as:
if (tConnect.getRunCounter() % 30 == 0) {
I've also changed the connect_timeout value to 90 seconnds
#define CONNECT_TIMEOUT 90 // Seconds
The serial monitor keeps on printing Serial.println(F(": connectCheck."));
for 30 seconds before re-requesting the connection.
Without the library, my hardware gets connected to the network in less than 10 seconds.
Can you please suggest some other troubleshooting step.
Thanks!
Hi, Thanks for the suggestion, but even this is not working. I've modified re-request interval as:
if (tConnect.getRunCounter() % 30 == 0) {
I've also changed the connect_timeout value to 90 seconnds
#define CONNECT_TIMEOUT 90 // Seconds
The serial monitor keeps on printing
Serial.println(F(": connectCheck."));
for 30 seconds before re-requesting the connection. Without the library, my hardware gets connected to the network in less than 10 seconds.Can you please suggest some other troubleshooting step.
Thanks!
Big Update!
When I changed if (tConnect.getRunCounter() % 30 == 0) {
to if (tConnect.getRunCounter() % 60 == 0) {
and increased the Timeout value to 100, it finally got connected to the network!
Thanks a lot for your help! You saved the day :)
Can you post your code which connects in 10 seconds? It is still a big difference between 10 and a 100! Should not be that long.
Here, on initiating the function InitWifi(), the esp gets connected to the wifi in no time.
#include <ArduinoJson.h>
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#define WIFI_SSID "myssid"
#define WIFI_PASSWORD "*************"
#define TOKEN "*******************"
#define GPIO0 1
#define GPIO2 2
#define GPIO0_PIN 3
#define GPIO2_PIN 5
char thingsboardServer[] = "demo.thingsboard.io";
WiFiClient wifiClient;
PubSubClient client(wifiClient);
int status = WL_IDLE_STATUS;
// We assume that all GPIOs are LOW
boolean gpioState[] = {false, false};
void setup() {
Serial.begin(115200);
// Set output mode for all GPIO pins
pinMode(GPIO0, OUTPUT);
pinMode(GPIO2, OUTPUT);
delay(10);
InitWiFi();
client.setServer( thingsboardServer, 1883 );
client.setCallback(on_message);
}
void loop() {
if ( !client.connected() ) {
reconnect();
}
client.loop();
}
// The callback for when a PUBLISH message is received from the server.
void on_message(const char* topic, byte* payload, unsigned int length) {
Serial.println("On message");
char json[length + 1];
strncpy (json, (char*)payload, length);
json[length] = '\0';
Serial.print("Topic: ");
Serial.println(topic);
Serial.print("Message: ");
Serial.println(json);
// Decode JSON request
StaticJsonBuffer<200> jsonBuffer;
JsonObject& data = jsonBuffer.parseObject((char*)json);
if (!data.success())
{
Serial.println("parseObject() failed");
return;
}
// Check request method
String methodName = String((const char*)data["method"]);
if (methodName.equals("getGpioStatus")) {
// Reply with GPIO status
String responseTopic = String(topic);
responseTopic.replace("request", "response");
client.publish(responseTopic.c_str(), get_gpio_status().c_str());
} else if (methodName.equals("setGpioStatus")) {
// Update GPIO status and reply
set_gpio_status(data["params"]["pin"], data["params"]["enabled"]);
String responseTopic = String(topic);
responseTopic.replace("request", "response");
client.publish(responseTopic.c_str(), get_gpio_status().c_str());
client.publish("v1/devices/me/attributes", get_gpio_status().c_str());
}
}
String get_gpio_status() {
// Prepare gpios JSON payload string
StaticJsonBuffer<200> jsonBuffer;
JsonObject& data = jsonBuffer.createObject();
data[String(GPIO0_PIN)] = gpioState[0] ? true : false;
data[String(GPIO2_PIN)] = gpioState[1] ? true : false;
char payload[256];
data.printTo(payload, sizeof(payload));
String strPayload = String(payload);
Serial.print("Get gpio status: ");
Serial.println(strPayload);
return strPayload;
}
void set_gpio_status(int pin, boolean enabled) {
if (pin == GPIO0_PIN) {
// Output GPIOs state
digitalWrite(GPIO0, enabled ? HIGH : LOW);
// Update GPIOs state
gpioState[0] = enabled;
} else if (pin == GPIO2_PIN) {
// Output GPIOs state
digitalWrite(GPIO2, enabled ? HIGH : LOW);
// Update GPIOs state
gpioState[1] = enabled;
}
}
void InitWiFi() {
Serial.println("Connecting to AP ...");
// attempt to connect to WiFi network
WiFi.begin(WIFI_AP, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(300);
Serial.print(".");
digitalWrite(GPIO0, LOW);
delay(200);
digitalWrite(GPIO0, HIGH);
}
Serial.println("Connected to AP");
digitalWrite(GPIO0, LOW);
delay(500);
digitalWrite(GPIO0, HIGH);
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
status = WiFi.status();
if ( status != WL_CONNECTED) {
WiFi.begin(WIFI_AP, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Connected to AP");
}
Serial.print("Connecting to ThingsBoard node ...");
// Attempt to connect (clientId, username, password)
if ( client.connect("ESP8266 Device", TOKEN, NULL) ) {
Serial.println( "[DONE]" );
// Subscribing to receive RPC requests
client.subscribe("v1/devices/me/rpc/request/+");
// Sending current GPIO status
Serial.println("Sending current GPIO status ...");
client.publish("v1/devices/me/attributes", get_gpio_status().c_str());
} else {
Serial.print( "[FAILED] [ rc = " );
Serial.print( client.state() );
Serial.println( " : retrying in 5 seconds]" );
// Wait 5 seconds before retrying
delay( 5000 );
}
}
}
I wonder if this version connects within 10 seconds because I don't give the WiFi stack enough time to do the connection work. Here you give it 300 - 500 ms via delay, while I am not using delay at all!
@arkhipenko You should always give the background processes some time to do stuff.
You may also consider adding delay(0)
to the code.
In most cases this will not add any noticeable delay to the execution of the code, but when there is something to be done, it will be done.
N.B. do not call delay
in callback functions.
After calling a connect to wifi it is best to perform a delay of about 103 msec, since the beacon interval is in most cases 102.4 msec, so there is almost no chance you will then miss it. Also on some boards (often cheap ones) you may see that the success rate of connecting increases when the ESP is not doing anything else while trying to connect to WiFi.
Thank you! That's a good point
I am doing yield()
:
WiFi.disconnect(true);
yield(); // This is an esp8266 standard yield to allow linux wifi stack run
WiFi.hostname("esp8266");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pwd);
yield();
but it obviously not enough. Probably WiFi connection related tasks do need to run as a separate dedicated section, and cooperative scheduling in earnest should start only after all connections are established.
In ESPeasy we use also some kind of scheduler I wrote myself. (before I saw this project)
In this scheduler I call delay(0)
at least once before executing things to be done.
But still, there are some reports where some nodes are not able to make a connection while there are tasks to be done during the connection phase. And it is quite specific which node never has issues and which one has, so it seems to be somewhat hardware related. (maybe power related, or some chip revision... I have no idea)
Well, the example above is compiled with #define _TASK_SLEEP_ON_IDLE_RUN
statement, which means that Scheduler itself should call delay(1)
every time there are no tasks to schedule (which is this case).
See here.
Since connection checking happens only once per second, delay(1)
should be called close to 1000 times!
Hi, I am using ESP01 board from AI Thinker and I am not able to connect to my wifi router when I execute the yield example(Schedule_example14_yield.ino ). I have changed the SSID and password. I have also removed one of the two LEDs and assigned "2" instead of the initial "D0" pin since I am not using the Node MCU. I am using a standard 2.4GHz wifi router. WiFi.begin() does connect me to the network when I don't use the TaskScheduler.h library.