Closed joeljana closed 1 month ago
// Uncomment the following line to enable serial debug output
//#define ENABLE_DEBUG
#ifdef ENABLE_DEBUG
#define DEBUG_ESP_PORT Serial
#define NODEBUG_WEBSOCKETS
#define NDEBUG
#endif
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include "SinricPro.h"
#include "SinricProSwitch.h"
#include <map>
#define WIFI_SSID "JOEL"
#define WIFI_PASS "mi123456"
#define APP_KEY "01583a71-43e5-450c-****************" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx"
#define APP_SECRET "b177b2a6-3de4-4653-81a1-02619ae7b*****************" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx"
//Enter the device IDs here
#define device_ID_1 "664527df*********e1f59f"
#define device_ID_2 "66452818********3e1f59f"
// define the GPIO connected with Relays and switches
#define RelayPin1 5
#define RelayPin2 4
#define SwitchPin1 14
#define SwitchPin2 12
#define wifiLed 16 //D0
// comment the following line if you use a toggle switches instead of tactile buttons
#define TACTILE_BUTTON 1
#define BAUD_RATE 115200
#define DEBOUNCE_TIME 250
typedef struct { // struct for the std::map below
int relayPIN;
int flipSwitchPIN;
} deviceConfig_t;
// this is the main configuration
// please put in your deviceId, the PIN for Relay and PIN for flipSwitch
// this can be up to N devices...depending on how much pin's available on your device ;)
// right now we have 4 devicesIds going to 4 relays and 4 flip switches to switch the relay manually
std::map<String, deviceConfig_t> devices = {
//{deviceId, {relayPIN, flipSwitchPIN}}
{device_ID_1, { RelayPin1, SwitchPin1 }},
{device_ID_2, { RelayPin2, SwitchPin2 }}
};
typedef struct { // struct for the std::map below
String deviceId;
bool lastFlipSwitchState;
unsigned long lastFlipSwitchChange;
} flipSwitchConfig_t;
std::map<int, flipSwitchConfig_t> flipSwitches; // this map is used to map flipSwitch PINs to deviceId and handling debounce and last flipSwitch state checks
// it will be setup in "setupFlipSwitches" function, using informations from devices map
void setupRelays() {
for (auto &device : devices) { // for each device (relay, flipSwitch combination)
int relayPIN = device.second.relayPIN; // get the relay pin
pinMode(relayPIN, OUTPUT); // set relay pin to OUTPUT
digitalWrite(relayPIN, HIGH);
}
}
void setupFlipSwitches() {
for (auto &device : devices) { // for each device (relay / flipSwitch combination)
flipSwitchConfig_t flipSwitchConfig; // create a new flipSwitch configuration
flipSwitchConfig.deviceId = device.first; // set the deviceId
flipSwitchConfig.lastFlipSwitchChange = 0; // set debounce time
flipSwitchConfig.lastFlipSwitchState = true; // set lastFlipSwitchState to false (LOW)--
int flipSwitchPIN = device.second.flipSwitchPIN; // get the flipSwitchPIN
flipSwitches[flipSwitchPIN] = flipSwitchConfig; // save the flipSwitch config to flipSwitches map
pinMode(flipSwitchPIN, INPUT_PULLUP); // set the flipSwitch pin to INPUT
}
}
bool onPowerState(String deviceId, bool &state)
{
Serial.printf("%s: %s\r\n", deviceId.c_str(), state ? "on" : "off");
int relayPIN = devices[deviceId].relayPIN; // get the relay pin for corresponding device
digitalWrite(relayPIN, !state); // set the new relay state
return true;
}
void handleFlipSwitches() {
unsigned long actualMillis = millis(); // get actual millis
for (auto &flipSwitch : flipSwitches) { // for each flipSwitch in flipSwitches map
unsigned long lastFlipSwitchChange = flipSwitch.second.lastFlipSwitchChange; // get the timestamp when flipSwitch was pressed last time (used to debounce / limit events)
if (actualMillis - lastFlipSwitchChange > DEBOUNCE_TIME) { // if time is > debounce time...
int flipSwitchPIN = flipSwitch.first; // get the flipSwitch pin from configuration
bool lastFlipSwitchState = flipSwitch.second.lastFlipSwitchState; // get the lastFlipSwitchState
bool flipSwitchState = digitalRead(flipSwitchPIN); // read the current flipSwitch state
if (flipSwitchState != lastFlipSwitchState) { // if the flipSwitchState has changed...
#ifdef TACTILE_BUTTON
if (flipSwitchState) { // if the tactile button is pressed
#endif
flipSwitch.second.lastFlipSwitchChange = actualMillis; // update lastFlipSwitchChange time
String deviceId = flipSwitch.second.deviceId; // get the deviceId from config
int relayPIN = devices[deviceId].relayPIN; // get the relayPIN from config
bool newRelayState = !digitalRead(relayPIN); // set the new relay State
digitalWrite(relayPIN, newRelayState); // set the trelay to the new state
SinricProSwitch &mySwitch = SinricPro[deviceId]; // get Switch device from SinricPro
mySwitch.sendPowerStateEvent(!newRelayState); // send the event
#ifdef TACTILE_BUTTON
}
#endif
flipSwitch.second.lastFlipSwitchState = flipSwitchState; // update lastFlipSwitchState
}
}
}
}
void setupWiFi()
{
Serial.printf("\r\n[Wifi]: Connecting");
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
handleFlipSwitches();
yield();
}
digitalWrite(wifiLed, LOW);
Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str());
}
//Setup Function For OTA Start
void setupOTA() {
// Hostname defaults to esp3232-[MAC]
ArduinoOTA.setHostname("SinricProOTATestDevice");
// No authentication by default
// ArduinoOTA.setPassword("admin");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.printf("\r\n[setupOTA]: Ready");
}
//Setup Function For OTA End
void setupSinricPro()
{
for (auto &device : devices)
{
const char *deviceId = device.first.c_str();
SinricProSwitch &mySwitch = SinricPro[deviceId];
mySwitch.onPowerState(onPowerState);
}
SinricPro.begin(APP_KEY, APP_SECRET);
SinricPro.restoreDeviceStates(true);
}
void setup()
{
Serial.begin(BAUD_RATE);
pinMode(wifiLed, OUTPUT);
digitalWrite(wifiLed, HIGH);
setupRelays();
setupFlipSwitches();
setupWiFi();
setupSinricPro();
setupOTA();
}
void loop()
{
ArduinoOTA.handle();
SinricPro.handle();
handleFlipSwitches();
}
At the time you turn on the MCU, if there's no WiFi it is stuck in this while loop. so your handleFlipSwitches();
never gets called.
while (WiFi.status() != WL_CONNECTED)
{
// Serial.printf(".");
// delay(250);
}
}
At the time you turn on the MCU, if there's no internet it is stuck in this while loop. so your
handleFlipSwitches();
never gets called.while (WiFi.status() != WL_CONNECTED) { // Serial.printf("."); // delay(250); } }
Thats why i comment delay(250);
. beacuse previously it was blocking whole loop when WL is not connected.
After remove delay(250);
the manual switches are working fine. even the MCU is not connected to any router.
But the problem is if the MCU is connected to any router without internet access the loop stuck at SinricPro.handle();
.
how to solve this problem sir?
Is there any way to prevent execute SinricPro.handle();
when there is no internet connection ?
Calling SinricPro.handle()
does not block the sketch. It's written in a non blocking manner.
Maybe ArduinoOTA.handle()
does when there is no WiFi connection. Comment this out and try again.
The while-loop in setup()
is definitely blocking, regardless of whether there is a delay
in it or not.
The while-loop istself blocks until WiFi is connected!
To make the buttons work while it is waiting for a WiFi connection simply change the while loop to:
while (WiFi.status() != WL_CONNECTED) {
handleFlipSwitches();
}
while (WiFi.status() != WL_CONNECTED) {
handleFlipSwitches();
yield();
}
Sir its helpful when MCU is not connected to any wifi.
Because some time internet disconnects from ISP sides. Then I have to switch off the router to satisfy this while-loop. Then I can use manual switches...
while (WiFi.status() != WL_CONNECTED) {
handleFlipSwitches();
yield();
}
I Comment out SinricPro.handle()
. Surprisingly manual switch works again as expexted.
Or Comment out SinricPro.begin(APP_KEY, APP_SECRET);
do the same job for me.
May be the SinricPro library's connection attempt to its servers, which can cause the sketch to hang if the internet connection is unavailable.
So how to fix this issue....???
How to handle SinricPro connection SinricPro.handle()
in a non-blocking manner When MCU is connected to the router but there's no internet access.
I did a test (one button + one LED as a replacement for the relay). I cannot confirm the error. The button switches the LED, regardless of whether there is an Internet connection or not.
However, I used an ESP32 as I no longer have an ESP8266.
It may be that the underlying WebSocket library behaves differently on an ESP8266 than on an ESP32. Unfortunately, I cannot test this due to the lack of an ESP8266.
In general: The ESP8266 has been discontinued by Espressif. I would therefore recommend that you switch to an ESP32 or better ESP32-S3.
Fortunately, I found an old ESP12e and I was able to test the same code here.
It does indeed block on the ESP8266. It is not a complete blockage, but there are delays of a few seconds.
However, the cause lies with the ESP8266 itself. I therefore repeat my recommendation to switch to the ESP32.
You could handle the code for the button via an interrupt routine. However, this must only switch the relay - not send the event! This could solve your problem on the ESP8266 - not tested.
Thank you for your response, sir.
You are right. The ESP32 works perfectly, likely because its extra core allows the WebSocket library and other tasks to run simultaneously and more efficiently than the single-core ESP8266.
Mentors like you always inspire us. Thanks again!
Controlling 2 relay using 2 push switch . working fine as expected.
Then I notice when the NodeMcu disconnected from the router the manual push switches are not working. Maybe the
delay(250);
in wifiSetup function was blocking the loop so i remove that from the while loop.Now im facing another issue . If Esp is connected to any router and but no internet connectivity during that time the manual push switch will stop working.
I think
SinricPro.handle();
In the void Loop Blocking others when there is no internet connectivity.Need help ...