429 lines
14 KiB
C++
429 lines
14 KiB
C++
|
//*******************************************************************************
|
||
|
// SmartThings NodeMCU ESP32 Wifi Library
|
||
|
//
|
||
|
// License
|
||
|
// (C) Copyright 2017 Dan Ogorchock
|
||
|
//
|
||
|
// History
|
||
|
// 2017-08-15 Dan Ogorchock Created with the help of Joshua Spain
|
||
|
// 2017-09-05 Dan Ogorchock Added automatic WiFi reconnect logic as ESP32
|
||
|
// doesn't do this automatically currently
|
||
|
// 2018-01-01 Dan Ogorchock Added WiFi.RSSI() data collection
|
||
|
// 2018-01-06 Dan Ogorchock Simplified the MAC address printout to prevent confusion
|
||
|
// 2018-02-03 Dan Ogorchock Support for Hubitat
|
||
|
// 2020-04-10 Dan Ogorchock Improved network performance by disabling WiFi Sleep
|
||
|
// 2020-06-20 Dan Ogorchock Add user selectable host name (repurposing the old shieldType variable)
|
||
|
//
|
||
|
//*******************************************************************************
|
||
|
|
||
|
#include "SmartThingsESP32WiFi.h"
|
||
|
|
||
|
namespace st
|
||
|
{
|
||
|
int SmartThingsESP32WiFi::disconnectCounter = 0;
|
||
|
|
||
|
//*******************************************************************************
|
||
|
// SmartThingsESP32WiFi Constructor - Static IP
|
||
|
//*******************************************************************************
|
||
|
SmartThingsESP32WiFi::SmartThingsESP32WiFi(String ssid, String password, IPAddress localIP, IPAddress localGateway, IPAddress localSubnetMask, IPAddress localDNSServer, uint16_t serverPort, IPAddress hubIP, uint16_t hubPort, SmartThingsCallout_t *callout, String shieldType, bool enableDebug, int transmitInterval) :
|
||
|
SmartThingsEthernet(localIP, localGateway, localSubnetMask, localDNSServer, serverPort, hubIP, hubPort, callout, shieldType, enableDebug, transmitInterval, false),
|
||
|
st_server(serverPort)
|
||
|
{
|
||
|
ssid.toCharArray(st_ssid, sizeof(st_ssid));
|
||
|
password.toCharArray(st_password, sizeof(st_password));
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
// SmartThingsESP32WiFI Constructor - DHCP
|
||
|
//*******************************************************************************
|
||
|
SmartThingsESP32WiFi::SmartThingsESP32WiFi(String ssid, String password, uint16_t serverPort, IPAddress hubIP, uint16_t hubPort, SmartThingsCallout_t *callout, String shieldType, bool enableDebug, int transmitInterval) :
|
||
|
SmartThingsEthernet(serverPort, hubIP, hubPort, callout, shieldType, enableDebug, transmitInterval, true),
|
||
|
st_server(serverPort)
|
||
|
{
|
||
|
ssid.toCharArray(st_ssid, sizeof(st_ssid));
|
||
|
password.toCharArray(st_password, sizeof(st_password));
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
// SmartThingsESP32WiFI Constructor - DHCP
|
||
|
//*******************************************************************************
|
||
|
SmartThingsESP32WiFi::SmartThingsESP32WiFi(uint16_t serverPort, IPAddress hubIP, uint16_t hubPort, SmartThingsCallout_t *callout, String shieldType, bool enableDebug, int transmitInterval) :
|
||
|
SmartThingsEthernet(serverPort, hubIP, hubPort, callout, shieldType, enableDebug, transmitInterval, true),
|
||
|
st_server(serverPort)
|
||
|
{
|
||
|
st_preExistingConnection = true;
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//SmartThingsESP32WiFI::~SmartThingsESP32WiFI()
|
||
|
//*****************************************************************************
|
||
|
SmartThingsESP32WiFi::~SmartThingsESP32WiFi()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************
|
||
|
/// Event Handler for ESP32 WiFi Events (needed to implement reconnect logic for now...)
|
||
|
//**************************************************************************************
|
||
|
void SmartThingsESP32WiFi::WiFiEvent(WiFiEvent_t event)
|
||
|
{
|
||
|
Serial.printf("[WiFi-event] event: %d\n", event);
|
||
|
|
||
|
switch (event) {
|
||
|
case SYSTEM_EVENT_STA_GOT_IP:
|
||
|
Serial.println("WiFi connected");
|
||
|
Serial.println("IP address: ");
|
||
|
Serial.println(WiFi.localIP());
|
||
|
break;
|
||
|
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||
|
Serial.println("WiFi lost connection. Attempting to reconnect...");
|
||
|
WiFi.reconnect();
|
||
|
disconnectCounter++;
|
||
|
if (disconnectCounter > 10) {
|
||
|
Serial.println("We have recieved the STA_DISCONNECTED event over 10 times now. Reboot...");
|
||
|
ESP.restart();
|
||
|
}
|
||
|
break;
|
||
|
case SYSTEM_EVENT_STA_START:
|
||
|
Serial.println("ESP32 station start");
|
||
|
break;
|
||
|
case SYSTEM_EVENT_STA_CONNECTED:
|
||
|
Serial.println("ESP32 station connected to AP");
|
||
|
disconnectCounter = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
/// Initialize SmartThingsESP32WiFI Library
|
||
|
//*******************************************************************************
|
||
|
void SmartThingsESP32WiFi::init(void)
|
||
|
{
|
||
|
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
||
|
|
||
|
// delete old config
|
||
|
WiFi.disconnect(true);
|
||
|
delay(1000);
|
||
|
WiFi.onEvent(SmartThingsESP32WiFi::WiFiEvent);
|
||
|
|
||
|
|
||
|
//Turn off Wirelss Access Point
|
||
|
Serial.println(F("Disabling ESP32 WiFi Access Point"));
|
||
|
Serial.println(F(""));
|
||
|
WiFi.mode(WIFI_STA);
|
||
|
//WiFi.setAutoReconnect(true);
|
||
|
//WiFi.setAutoConnect(true);
|
||
|
WiFi.setSleep(false);
|
||
|
|
||
|
//get the MAC address
|
||
|
String strMAC(WiFi.macAddress());
|
||
|
strMAC.replace(":", "");
|
||
|
|
||
|
//Set the hostname
|
||
|
if (_shieldType == "ESP32Wifi") {
|
||
|
String("ESP32-" + strMAC).toCharArray(st_devicename, sizeof(st_devicename));
|
||
|
}
|
||
|
else {
|
||
|
_shieldType.toCharArray(st_devicename, sizeof(st_devicename));
|
||
|
}
|
||
|
Serial.print(F("hostName = "));
|
||
|
Serial.println(st_devicename);
|
||
|
|
||
|
bool result = WiFi.setHostname(st_devicename);
|
||
|
Serial.print(F("setHostname returned "));
|
||
|
Serial.println(result);
|
||
|
|
||
|
if (st_DHCP == false)
|
||
|
{
|
||
|
WiFi.config(st_localIP, st_localGateway, st_localSubnetMask, st_localDNSServer);
|
||
|
}
|
||
|
|
||
|
if (!st_preExistingConnection) {
|
||
|
Serial.println(F(""));
|
||
|
Serial.println(F("Initializing ESP32 WiFi network. Please be patient..."));
|
||
|
//wait for ESP32 to be ready on startup
|
||
|
delay(1000); //may not be necessary, but seems to help my test board start up cleanly
|
||
|
|
||
|
// attempt to connect to WiFi network
|
||
|
WiFi.begin(st_ssid, st_password);
|
||
|
Serial.print(F("Attempting to connect to WPA SSID: "));
|
||
|
Serial.println(st_ssid);
|
||
|
}
|
||
|
|
||
|
int count =0;
|
||
|
while (WiFi.status() != WL_CONNECTED) {
|
||
|
count++;
|
||
|
Serial.print(F("."));
|
||
|
delay(500); // wait for connection:
|
||
|
if (count > 10) {
|
||
|
Serial.println(F("what is taking so long?"));
|
||
|
count = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Serial.println();
|
||
|
|
||
|
st_server.begin();
|
||
|
|
||
|
Serial.println(F(""));
|
||
|
Serial.println(F("Enter the following three lines of data into ST App on your phone!"));
|
||
|
Serial.print(F("localIP = "));
|
||
|
Serial.println(WiFi.localIP());
|
||
|
Serial.print(F("serverPort = "));
|
||
|
Serial.println(st_serverPort);
|
||
|
Serial.print(F("MAC Address = "));
|
||
|
Serial.println(strMAC);
|
||
|
Serial.println(F(""));
|
||
|
Serial.print(F("SSID = "));
|
||
|
Serial.println(st_ssid);
|
||
|
Serial.print(F("PASSWORD = "));
|
||
|
Serial.println(st_password);
|
||
|
Serial.print(F("hubIP = "));
|
||
|
Serial.println(st_hubIP);
|
||
|
Serial.print(F("hubPort = "));
|
||
|
Serial.println(st_hubPort);
|
||
|
Serial.print(F("RSSI = "));
|
||
|
Serial.println(WiFi.RSSI());
|
||
|
Serial.println(F(""));
|
||
|
Serial.println(F("SmartThingsESP32WiFI: Intialized"));
|
||
|
Serial.println(F(""));
|
||
|
|
||
|
RSSIsendInterval = 5000;
|
||
|
previousMillis = millis() - RSSIsendInterval;
|
||
|
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
// Run SmartThingsESP32WiFI Library
|
||
|
//*****************************************************************************
|
||
|
void SmartThingsESP32WiFi::run(void)
|
||
|
{
|
||
|
String readString;
|
||
|
String tempString;
|
||
|
String strRSSI;
|
||
|
|
||
|
if (WiFi.isConnected() == false)
|
||
|
{
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.println(F("**********************************************************"));
|
||
|
Serial.println(F("**** WiFi Disconnected. ESP32 should auto-reconnect. ***"));
|
||
|
Serial.println(F("**********************************************************"));
|
||
|
}
|
||
|
//WiFi.reconnect();
|
||
|
//init();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (millis() - previousMillis > RSSIsendInterval)
|
||
|
{
|
||
|
|
||
|
previousMillis = millis();
|
||
|
|
||
|
if (RSSIsendInterval < RSSI_TX_INTERVAL)
|
||
|
{
|
||
|
RSSIsendInterval = RSSIsendInterval + 1000;
|
||
|
}
|
||
|
|
||
|
strRSSI = String("rssi ") + String(WiFi.RSSI());
|
||
|
send(strRSSI);
|
||
|
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.println(strRSSI);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WiFiClient client = st_server.available();
|
||
|
if (client) {
|
||
|
boolean currentLineIsBlank = true;
|
||
|
while (client.connected()) {
|
||
|
if (client.available()) {
|
||
|
char c = client.read();
|
||
|
//read char by char HTTP request
|
||
|
if (readString.length() < 200) {
|
||
|
//store characters to string
|
||
|
readString += c;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.println(F(""));
|
||
|
Serial.println(F("SmartThings.run() - Exceeded 200 character limit"));
|
||
|
Serial.println(F(""));
|
||
|
}
|
||
|
}
|
||
|
// if you've gotten to the end of the line (received a newline
|
||
|
// character) and the line is blank, the http request has ended,
|
||
|
// so you can send a reply
|
||
|
if (c == '\n' && currentLineIsBlank) {
|
||
|
//now output HTML data header
|
||
|
tempString = readString.substring(readString.indexOf('/') + 1, readString.indexOf('?'));
|
||
|
|
||
|
if (tempString.length() > 0) {
|
||
|
client.println(F("HTTP/1.1 200 OK")); //send new page
|
||
|
client.println();
|
||
|
}
|
||
|
else {
|
||
|
client.println(F("HTTP/1.1 204 No Content"));
|
||
|
client.println();
|
||
|
client.println();
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.println(F("No Valid Data Received"));
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (c == '\n') {
|
||
|
// you're starting a new line
|
||
|
currentLineIsBlank = true;
|
||
|
}
|
||
|
else if (c != '\r') {
|
||
|
// you've gotten a character on the current line
|
||
|
currentLineIsBlank = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delay(1);
|
||
|
//stopping client
|
||
|
client.stop();
|
||
|
|
||
|
//Handle the received data after cleaning up the network connection
|
||
|
if (tempString.length() > 0) {
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.print(F("Handling request from ST. tempString = "));
|
||
|
Serial.println(tempString);
|
||
|
}
|
||
|
//Pass the message to user's SmartThings callout function
|
||
|
tempString.replace("%20", " "); //Clean up for Hubitat
|
||
|
_calloutFunction(tempString);
|
||
|
}
|
||
|
|
||
|
readString = "";
|
||
|
tempString = "";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
/// Send Message out over Ethernet to the Hub
|
||
|
//*******************************************************************************
|
||
|
void SmartThingsESP32WiFi::send(String message)
|
||
|
{
|
||
|
if (WiFi.isConnected() == false)
|
||
|
{
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.println(F("**********************************************************"));
|
||
|
Serial.println(F("**** WiFi Disconnected. ESP32 should auto-reconnect. ***"));
|
||
|
Serial.println(F("**********************************************************"));
|
||
|
}
|
||
|
|
||
|
//WiFi.reconnect();
|
||
|
//init();
|
||
|
}
|
||
|
|
||
|
//WiFiClient st_client;
|
||
|
|
||
|
//Make sure the client is stopped, to free up socket for new connection
|
||
|
st_client.stop();
|
||
|
|
||
|
if (st_client.connect(st_hubIP, st_hubPort))
|
||
|
{
|
||
|
st_client.println(F("POST / HTTP/1.1"));
|
||
|
st_client.print(F("HOST: "));
|
||
|
st_client.print(st_hubIP);
|
||
|
st_client.print(F(":"));
|
||
|
st_client.println(st_hubPort);
|
||
|
st_client.println(F("CONTENT-TYPE: text"));
|
||
|
st_client.println(F("CONNECTION: CLOSE"));
|
||
|
st_client.print(F("CONTENT-LENGTH: "));
|
||
|
st_client.println(message.length());
|
||
|
st_client.println();
|
||
|
st_client.print(message);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//connection failed;
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.println(F("***********************************************************"));
|
||
|
Serial.println(F("***** SmartThings.send() - Ethernet Connection Failed *****"));
|
||
|
Serial.println(F("***********************************************************"));
|
||
|
Serial.print(F("hubIP = "));
|
||
|
Serial.print(st_hubIP);
|
||
|
Serial.print(F(" "));
|
||
|
Serial.print(F("hubPort = "));
|
||
|
Serial.println(st_hubPort);
|
||
|
|
||
|
Serial.println(F("***********************************************************"));
|
||
|
Serial.println(F("**** WiFi Disconnected. ESP32 should auto-reconnect. ***"));
|
||
|
Serial.println(F("***********************************************************"));
|
||
|
}
|
||
|
|
||
|
//WiFi.reconnect();
|
||
|
//init(); //Re-Init connection to get things working again
|
||
|
|
||
|
if (_isDebugEnabled)
|
||
|
{
|
||
|
Serial.println(F("***********************************************************"));
|
||
|
Serial.println(F("****** Attempting to resend missed data *******"));
|
||
|
Serial.println(F("***********************************************************"));
|
||
|
}
|
||
|
|
||
|
|
||
|
st_client.flush();
|
||
|
st_client.stop();
|
||
|
if (st_client.connect(st_hubIP, st_hubPort))
|
||
|
{
|
||
|
st_client.println(F("POST / HTTP/1.1"));
|
||
|
st_client.print(F("HOST: "));
|
||
|
st_client.print(st_hubIP);
|
||
|
st_client.print(F(":"));
|
||
|
st_client.println(st_hubPort);
|
||
|
st_client.println(F("CONTENT-TYPE: text"));
|
||
|
st_client.print(F("CONTENT-LENGTH: "));
|
||
|
st_client.println(message.length());
|
||
|
st_client.println();
|
||
|
st_client.println(message);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// Wait for a response
|
||
|
unsigned long timeout = millis();
|
||
|
while(!st_client.available())
|
||
|
{
|
||
|
if(millis() - timeout > 1000)
|
||
|
{
|
||
|
Serial.println(F("Post request timed out\n"));
|
||
|
st_client.stop();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//if (_isDebugEnabled) { Serial.println(F("WiFi.send(): Reading for reply data "));}
|
||
|
// read any data returned from the POST
|
||
|
//while (st_client.connected()) {
|
||
|
while (st_client.available() && st_client.connected()) {
|
||
|
char c = st_client.read(); //gets byte from ethernet buffer
|
||
|
/*if((int) c == 255)
|
||
|
{
|
||
|
if(_isDebugEnabled)
|
||
|
Serial.println(F("Breaking due to invalid value"));
|
||
|
break;
|
||
|
}*/
|
||
|
if (_isDebugEnabled) { Serial.print(c); } //prints byte to serial monitor
|
||
|
}
|
||
|
|
||
|
delay(1);
|
||
|
st_client.stop();
|
||
|
}
|
||
|
}
|