st-anything/lib/ST_Anything/Everything.cpp
Gašper Dobrovoljc ec125f27db
IP Configuration
2023-03-11 15:11:03 +01:00

429 lines
13 KiB
C++

//******************************************************************************************
// File: Everything.cpp
// Authors: Dan G Ogorchock & Daniel J Ogorchock (Father and Son)
//
// Summary: st::Everything is a generic class which essentially acts as the main() routine.
// All st::Device type objects are managed by st::Everything. It is responsible for
// for calling the correct functions within each object it is responsible for at the
// proper time. It handles all initialization of and use of the SmarThings Shield library.
//
// THere are user-definable settings which will impact the st::Everything class stored in
// Constants.h. Please edit Constants.h to adjust these settings.
//
// In general, this file should not need to be modified.
//
// Change History:
//
// Date Who What
// ---- --- ----
// 2015-01-03 Dan & Daniel Original Creation
// 2015-01-10 Dan Ogorchock Minor improvements to support Door Control Capability
// 2015-03-14 Dan Ogorchock Added public setLED() function to control ThingShield LED
// 2015-03-28 Dan Ogorchock Added throttling capability to sendStrings to improve success rate of ST Cloud getting the data ("SENDSTRINGS_INTERVAL" is in CONSTANTS.H)
// 2017-02-07 Dan Ogorchock Added support for new SmartThings v2.0 library (ThingShield, W5100, ESP8266)
// 2017-02-19 Dan Ogorchock Fixed bug in throttling capability
// 2017-04-26 Dan Ogorchock Allow each communication method to specify unique ST transmission throttling delay
// 2019-02-09 Dan Ogorchock Add update() call to Executors in support of devices like EX_Servo that need a non-blocking mechanism
// 2019-02-24 Dan Ogorchock Added new special callOnMsgRcvd2 callback capability. Allows recvd string to be manipulated in the sketch before being processed by Everything.
// 2021-01-31 Marcus van Ierssel Improved the automatic refresh to prevent it from blocking other updates.
// 2021-05-23 Dan Ogorchock Address EXP8266 v3.0.0 board support package compatibility issue with Strings
//
//******************************************************************************************
//#include <Arduino.h>
//#include <avr/pgmspace.h>
#include "Everything.h"
long freeRam(); //freeRam() function prototype - useful in determining how much SRAM is available on Arduino
#if defined(ARDUINO_ARCH_SAMD)
extern "C" char* sbrk(int incr);
#endif
namespace st
{
//private
void Everything::updateDevices()
{
for(unsigned int index=0; index<m_nSensorCount; ++index)
{
m_Sensors[index]->update();
sendStrings();
}
for (unsigned int i = 0; i<m_nExecutorCount; ++i)
{
m_Executors[i]->update();
sendStrings();
}
}
#if defined(ENABLE_SERIAL)
void Everything::readSerial()
{
String message;
while(Serial.available()>0)
{
char c=Serial.read();
message+=c;
delay(10);
}
if(message.length()>0)
{
receiveSmartString(message);
}
}
#endif
void Everything::sendStrings()
{
unsigned int index;
//Loop through the Return_String buffer and send each "|" delimited string to ST Shield
while(Return_String.length()>=1 && Return_String[0]!='|')
{
index=Return_String.indexOf("|");
if(debug)
{
Serial.print(F("Everything: Sending: "));
Serial.println(Return_String.substring(0, index));
//Serial.print(F("Everything: getTransmitInterval() = "));
//Serial.println(SmartThing->getTransmitInterval());
}
#ifndef DISABLE_SMARTTHINGS
// if (millis() - sendstringsLastMillis < Constants::SENDSTRINGS_INTERVAL)
if (millis() - sendstringsLastMillis < SmartThing->getTransmitInterval())
{
// delay(Constants::SENDSTRINGS_INTERVAL - (millis() - sendstringsLastMillis)); //Added due to slow ST Hub/Cloud Processing. Events were being missed. DGO 2015-03-28
delay(SmartThing->getTransmitInterval() - (millis() - sendstringsLastMillis)); //modified to allow different values for each method of communicating to ST cloud. DGO 2017-04-26
}
SmartThing->send(Return_String.substring(0, index));
sendstringsLastMillis = millis();
#endif
#if defined(ENABLE_SERIAL) && defined(DISABLE_SMARTTHINGS)
Serial.println(Return_String.substring(0, index));
#endif
if(callOnMsgSend!=0)
{
callOnMsgSend(Return_String.substring(0, index));
}
Return_String=Return_String.substring(index+1);
}
Return_String.remove(0); //clear the Return_String buffer
}
void Everything::refreshDevices()
{
static int refresh_Executor = 0;
static int refresh_Sensor = 0;
/*
for(unsigned int i=0; i<m_nExecutorCount; ++i)
{
m_Executors[i]->refresh();
sendStrings();
}
for (unsigned int i = 0; i<m_nSensorCount; ++i)
{
m_Sensors[i]->refresh();
sendStrings();
}
*/
if (refresh_Executor < m_nExecutorCount) {
m_Executors[refresh_Executor]->refresh();
sendStrings();
refresh_Executor++;
refLastMillis = millis() - long(Constants::DEV_REFRESH_INTERVAL) * 1000 + 4 * SmartThing->getTransmitInterval();
}
else if (refresh_Sensor < m_nSensorCount) {
m_Sensors[refresh_Sensor]->refresh();
sendStrings();
refresh_Sensor++;
refLastMillis = millis() - long(Constants::DEV_REFRESH_INTERVAL) * 1000 + 4 * SmartThing->getTransmitInterval();
}
else {
refLastMillis = millis();
refresh_Executor = 0;
refresh_Sensor = 0;
}
}
//public
void Everything::init()
{
Serial.begin(Constants::SERIAL_BAUDRATE);
Return_String.reserve(st::Constants::RETURN_STRING_RESERVE); //allocate Return_String buffer one time to prevent Heap Fragmentation. RETURN_STRING_RESERVE is set in Constants.h
if(debug)
{
Serial.println(F("Everything: init started"));
Serial.print(F("Everything: Free RAM = "));
Serial.println(freeRam());
}
#ifndef DISABLE_SMARTTHINGS
SmartThing->init();
#endif
if(debug)
{
Serial.println(F("Everything: init ended"));
Serial.print(F("Everything: Free RAM = "));
Serial.println(freeRam());
}
}
void Everything::initDevices()
{
if(debug)
{
Serial.println(F("Everything: initDevices started"));
Serial.print(F("Everything: Free RAM = "));
Serial.println(freeRam());
}
for(unsigned int index=0; index<m_nSensorCount; ++index)
{
m_Sensors[index]->init();
sendStrings();
}
for(unsigned int index=0; index<m_nExecutorCount; ++index)
{
m_Executors[index]->init();
sendStrings();
}
if(debug)
{
Serial.println(F("Everything: initDevices ended"));
Serial.print(F("Everything: Free RAM = "));
Serial.println(freeRam());
}
refLastMillis = millis(); //avoid immediately refreshing after initialization
}
void Everything::run()
{
updateDevices(); //call each st::Sensor object to refresh data
#ifndef DISABLE_SMARTTHINGS
SmartThing->run(); //call the ST Shield Library to receive any data from the ST Hub
#endif
#if defined(ENABLE_SERIAL)
readSerial(); //read data from the Arduino IDE Serial Monitor window (useful for debugging sometimes)
#endif
sendStrings(); //send any pending updates to ST Cloud
#ifndef DISABLE_REFRESH //Added new check to allow user to disable REFRESH feature - setting is in Constants.h)
if ((bTimersPending == 0) && ((millis() - refLastMillis) >= long(Constants::DEV_REFRESH_INTERVAL) * 1000)) //DEV_REFRESH_INTERVAL is set in Constants.h
{
//refLastMillis = millis();
refreshDevices(); //call each st::Device object to refresh data (this is just a safeguard to ensure the state of the Arduino and the ST Cloud stay in synch should an event be missed)
}
#endif
if((debug) && (millis()%60000==0) && (millis()!=lastmillis))
{
lastmillis = millis();
Serial.print(F("Everything: Free Ram = "));
Serial.println(freeRam());
}
}
bool Everything::sendSmartString(const String &str)
{
//while(str.length()>1 && str[0]=='|') //get rid of leading pipes (messes up sendStrings()'s parsing technique)
//{
// str=str.substring(1);
//}
if((str.length()==1 && str[0]=='|') || str.length()==0)
{
return false;
}
if(Return_String.length()+str.length()>=Constants::RETURN_STRING_RESERVE)
{
if (debug)
{
Serial.print(F("Everything: ERROR: \""));
Serial.print(str);
Serial.println(F("\" would overflow the Return_String 'buffer'"));
}
return false;
}
else
{
Return_String+=str+"|"; //add the new message to the queue to be sent to ST Shield with a "|" delimiter
return true;
}
}
bool Everything::sendSmartStringNow(const String &str)
{
bool queued = sendSmartString(str);
if (queued) sendStrings(); //send any pending updates to ST Cloud immediately
return queued;
}
Device* Everything::getDeviceByName(const String &str)
{
for(unsigned int index=0; index<m_nSensorCount; ++index)
{
if(m_Sensors[index]->getName()==str)
return (Device*)m_Sensors[index];
}
for(unsigned int index=0; index<m_nExecutorCount; ++index)
{
if(m_Executors[index]->getName()==str)
return (Device*)m_Executors[index];
}
return 0; //null if no such device present
}
bool Everything::addSensor(Sensor *sensor)
{
if(m_nSensorCount>=Constants::MAX_SENSOR_COUNT)
{
if(debug)
{
Serial.print(F("Did not add sensor named "));
Serial.print(sensor->getName());
Serial.println(F("(You've exceeded maximum number of sensors; edit Constants.h)"));
}
return false;
}
else
{
m_Sensors[m_nSensorCount]=sensor;
++m_nSensorCount;
}
if(debug)
{
Serial.print(F("Everything: adding sensor named "));
Serial.println(sensor->getName());
Serial.print(F("Everything: Free RAM = "));
Serial.println(freeRam());
}
return true;
}
bool Everything::addExecutor(Executor *executor)
{
if(m_nExecutorCount>=Constants::MAX_EXECUTOR_COUNT)
{
if(debug)
{
Serial.print(F("Did not add executor named "));
Serial.print(executor->getName());
Serial.println(F("(You've exceeded maximum number of executors; edit Constants.h)"));
}
return false;
}
else
{
m_Executors[m_nExecutorCount]=executor;
++m_nExecutorCount;
}
if(debug)
{
Serial.print(F("Everything: adding executor named "));
Serial.println(executor->getName());
Serial.print(F("Everything: Free RAM = "));
Serial.println(freeRam());
}
return true;
}
//friends!
void receiveSmartString(String message)
{
message.trim();
if(Everything::debug && message.length()>1)
{
Serial.print(F("Everything: Received: "));
Serial.println(message);
}
if (Everything::callOnMsgRcvd2 != 0)
{
Everything::callOnMsgRcvd2(message);
}
if (message == "refresh")
{
Everything::refreshDevices();
}
else if (message.length() > 1) //ignore empty string messages from the ST Hub
{
Device *p = Everything::getDeviceByName(message.substring(0, message.indexOf(' ')));
if (p != 0)
{
p->beSmart(message); //pass the incoming SmartThings Shield message to the correct Device's beSmart() routine
}
}
if(Everything::callOnMsgRcvd!=0)
{
Everything::callOnMsgRcvd(message);
}
}
//initialize static members
st::SmartThings* Everything::SmartThing=0; //initialize pointer to null
String Everything::Return_String;
Sensor* Everything::m_Sensors[Constants::MAX_SENSOR_COUNT];
Executor* Everything::m_Executors[Constants::MAX_EXECUTOR_COUNT];
byte Everything::m_nSensorCount=0;
byte Everything::m_nExecutorCount=0;
unsigned long Everything::lastmillis=0;
unsigned long Everything::refLastMillis=0;
unsigned long Everything::sendstringsLastMillis=0;
bool Everything::debug=false;
byte Everything::bTimersPending=0; //initialize variable
void (*Everything::callOnMsgSend)(const String &msg)=0; //initialize this callback function to null
void (*Everything::callOnMsgRcvd)(const String &msg)=0; //initialize this callback function to null
void(*Everything::callOnMsgRcvd2)(String &msg) = 0; //initialize this callback function to null
//SmartThings static members
//#ifndef DISABLE_SMARTTHINGS
// // Please refer to Constants.h for settings that affect whether a board uses SoftwareSerial or Hardware Serial calls
// #if defined(ST_SOFTWARE_SERIAL) //use Software Serial
// SmartThingsThingShield Everything::SmartThing(Constants::pinRX, Constants::pinTX, receiveSmartString);
// #elif defined(ST_HARDWARE_SERIAL) //use Hardware Serial
// SmartThingsThingShield Everything::SmartThing(Constants::SERIAL_TYPE, receiveSmartString);
// #endif
// SmartThingsNetworkState_t Everything::stNetworkState=(SmartThingsNetworkState_t)99; //bogus value for first pass through Everything::updateNetworkState()
//#endif
}
//freeRam() function - useful in determining how much SRAM is available on Arduino
long freeRam()
{
#if defined(ARDUINO_ARCH_AVR)
extern int __heap_start, *__brkval;
int v;
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
return ESP.getFreeHeap();
#elif defined(ARDUINO_ARCH_SAMD)
char top;
return &top - reinterpret_cast<char*>(sbrk(0));
#else
return -1;
#endif // !
}