[Release] HubDuino v1.1.9 - Hubitat to Arduino / ESP8266 / ESP32 / ThingShield Integration (ST_Anything)

That note would still be applicable to the original ESP32 microcontroller boards.

The ESP32 S3, and many other variants, are much newer models and may not have the GPIO pin restrictions.

The reason for the pin restrictions has nothing to do with ST_Anything. It is driven by the Espressif Hardware Design for its microcontrollers.

Please take a look at the following document for the ESP32 S3 Mini, in particular the 'Safe pins to use section'.

ESP32-S3 Super Mini Development Board Details, Pinout, Specs

and the Pins to Avoid section

@ogiewon
wow super fast reply, thanks Dan.
Very helpful info, including that resources link I hadnt stumbled over yet.

I'm new to these ESP32 packages sorry, so this idea that certain pins can be "risky" I havent wrapped my head around yet.
So before I go beta testing the whole HubDuino setup along with transferring my breadboard setup to prototype PCBs ... wanted to make sure I wasnt barking up the wrong tree with my chip selection.

As it is, I need 4 Ins and 4 Outs to run my AC ducted system Damper controls ... so looks like I'll have to take the risk of using GIO3, with all other safe "pins" only being tiny surface mount pads under the board.
Thanks again.

1 Like

Installed everything and without editing any files, I compiled the "ST_Anything_Multiples_ESP32S3ETH" sketch as a test and I'm sure there was no errors.
But oddly every sketch I compile, including this "ETH" sketch again, now gives me compilation errors.
Any thoughts on what changed and how to fix?
Pretty sure the only thing I did between the first & second compile was to install all the drivers on my Hubitat which should obviously have no impact.
(IDE 2.3.7)

EDIT: below was for "ST_Anything_Multiples_ESP32WiFi"

In file included from C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\OneWire.cpp:144:
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h: In function 'uint32_t directRead(uint32_t)':
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:134:17: error: 'GPIO' was not declared in this scope
  134 |         return (GPIO.in >> pin) & 0x1;
      |                 ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:136:17: error: 'GPIO' was not declared in this scope
  136 |         return (GPIO.in1.val >> (pin - 32)) & 0x1;
      |                 ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h: In function 'void directWriteLow(uint32_t)':
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:149:9: error: 'GPIO' was not declared in this scope
  149 |         GPIO.out_w1tc = ((uint32_t)1 << pin);
      |         ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:151:9: error: 'GPIO' was not declared in this scope
  151 |         GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32));
      |         ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h: In function 'void directWriteHigh(uint32_t)':
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:162:9: error: 'GPIO' was not declared in this scope
  162 |         GPIO.out_w1ts = ((uint32_t)1 << pin);
      |         ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:164:9: error: 'GPIO' was not declared in this scope
  164 |         GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32));
      |         ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h: In function 'void directModeInput(uint32_t)':
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:187:13: error: 'GPIO' was not declared in this scope
  187 |             GPIO.enable_w1tc = ((uint32_t)1 << pin);
      |             ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:189:13: error: 'GPIO' was not declared in this scope
  189 |             GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32));
      |             ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h: In function 'void directModeOutput(uint32_t)':
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:213:13: error: 'GPIO' was not declared in this scope
  213 |             GPIO.enable_w1ts = ((uint32_t)1 << pin);
      |             ^~~~
C:\Users\Gnat6\AppData\Local\Arduino15\libraries\OneWire\util/OneWire_direct_gpio.h:215:13: error: 'GPIO' was not declared in this scope
  215 |             GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32));
      |             ^~~~
Multiple libraries were found for "WiFi.h"
  Used: C:\Users\Gnat6\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\WiFi
  Not used: C:\Users\Gnat6\AppData\Local\Arduino15\libraries\WiFiNINA
exit status 1

Compilation error: exit status 1

Good news! You're not going crazy. I have just reproduced the same issue when using the code from my GitHub repo. I'll dig into it and see what has changed. Shouldn't take too long.

Looks like the issue was caused by some of the older versions of libraries that are included in the ST_Anything's Arduino\Libraries folder. Please pull down the latest .ZIP of the ST_Anything repository, and then replace all of the libraries with the new ones. Do not simply copy over top of the old libraries, as that will leave quite a mess behind. You would need to delete all of the libraries that are part of ST_Anything and then copy in the new ones.

Or, you can simply use the Arduino IDE's built-in Library manager to upgrade each of the "Updatable" libraries. When done, your Arduino IDE should look like the following.

Please let me know if this resolves the compilation issue.

This was a good opportunity for me to refresh all of the libraries that ST_Anything utilizes. Hopefully, there will not be any unforeseen issues that arise as a result.

Thanks Dan @ogiewon
Manual reinstall of the refreshed Libraries seems to have worked.
Compiled several of your sketches, no errors.

Oddly in my library manager with the "Updatable" filter shows absolutely nothing.

... now I just need to edit a sketch and work out how to get my Hubitat to acknowledge my ESP32. Oddly no how-to YT videos ... my normal go-to tutorials :slight_smile:

1 Like

So far so good, I have 4 contacts and 4 relays visible and controlled from Hubitat. YA!
But further code I'm running inside void loop() is manually driving the pins/connected relays, but Hubitat is not correctly showing this manual/ESP32 driven state (i.e. no change).
As example I'm driving a pin with simple "digitalWrite(2, HIGH);" but is there another way to push the pin state so that it also feeds or reads that state into the HubDuino/ST_Anything drivers/into Hubitat?

Sample of working sketch definitions

#define PIN_CONTACT_1             8  //SmartThings Capability "Contact Sensor"
#define PIN_CONTACT_2             9  //SmartThings Capability "Contact Sensor"
#define PIN_CONTACT_3             10  //SmartThings Capability "Contact Sensor"
#define PIN_CONTACT_4             11  //SmartThings Capability "Contact Sensor"
#define PIN_SWITCH_1              1  //SmartThings Capability "Switch"
#define PIN_SWITCH_2              2  //SmartThings Capability "Switch"
#define PIN_SWITCH_3              3  //SmartThings Capability "Switch"
#define PIN_SWITCH_4              5  //SmartThings Capability "Switch"
--------
  static st::IS_Contact             sensor1(F("contact1"), PIN_CONTACT_1, LOW, true, 500);
  static st::IS_Contact             sensor2(F("contact2"), PIN_CONTACT_2, LOW, true, 500);
  static st::IS_Contact             sensor3(F("contact3"), PIN_CONTACT_3, LOW, true, 500);
  static st::IS_Contact             sensor4(F("contact4"), PIN_CONTACT_4, LOW, true, 500);
  static st::EX_Switch              executor1(F("switch1"), PIN_SWITCH_1, LOW, true);
  static st::EX_Switch              executor2(F("switch2"), PIN_SWITCH_2, LOW, true);
  static st::EX_Switch              executor3(F("switch3"), PIN_SWITCH_3, LOW, true);
  static st::EX_Switch              executor4(F("switch4"), PIN_SWITCH_4, LOW, true);
---------
  st::Everything::addSensor(&sensor1);
  st::Everything::addSensor(&sensor2);
  st::Everything::addSensor(&sensor3);
  st::Everything::addSensor(&sensor4);
  st::Everything::addExecutor(&executor1);
  st::Everything::addExecutor(&executor2);
  st::Everything::addExecutor(&executor3);
  st::Everything::addExecutor(&executor4);


Yes, there is another way.

Let me explain - within an ST_Anything based Arduiono sketch, you've already noticed that one needs to create 'devices' that represent their matching counterparts on the Hubitat side of things:

  static st::IS_Contact             sensor1(F("contact1"), PIN_CONTACT_1, LOW, true, 500);
  static st::IS_Contact             sensor2(F("contact2"), PIN_CONTACT_2, LOW, true, 500);
  static st::IS_Contact             sensor3(F("contact3"), PIN_CONTACT_3, LOW, true, 500);
  static st::IS_Contact             sensor4(F("contact4"), PIN_CONTACT_4, LOW, true, 500);
  static st::EX_Switch              executor1(F("switch1"), PIN_SWITCH_1, LOW, true);
  static st::EX_Switch              executor2(F("switch2"), PIN_SWITCH_2, LOW, true);
  static st::EX_Switch              executor3(F("switch3"), PIN_SWITCH_3, LOW, true);
  static st::EX_Switch              executor4(F("switch4"), PIN_SWITCH_4, LOW, true);

Each of these are C++ objects, like their Hubitat device counterparts, have 'commands' and 'attributes' that must be properly utilized for everything to work harmoniously. By simply controlling the GPIO pins directly, you're currently bypassing things.

Please explain what you're trying to accomplish in the loop() routine. There are a couple of ways of doing things in an ST_Anything sketch, so it is important to know what your requirements are.

Thanks Dan.
I have a manual pushbutton 4 channel ducted AC Zone controller that I want to remote control via Hubitat.

Its controls are simple, each channel is;

  • 1 LED that indicates Zone Open(LED on) & Closed(LED off)
  • 1 momentary pushbutton, press to TOGGLE Zone/actuator Open/Closed
  • Output: 24VAC actuator motor on 3 wires; 1 wire common, 2 for each direction

I've directly tapped off each LED as INPUT (sensor/contact) on the ESP32.
This gives me the Open/Closed status of the Zones on the Hubitat. Good.

To remotely control the 4 Zones I'm using 4 relays to parallel the contacts on the actual pushbuttons.
Those relays are controlled from 4 OUTPUT (executor/switch) on the ESP32.

As it is now, this HubDuino setup does allow me in Hubitat to see the Open/Closed status of each Zone.
Plus to use 4 virtual buttons in Hubitat to toggle the 4 relays.
But for one thing this requires me to enable Auto-Off on the Hubitat buttons to simulate Momentary action.
I'd prefer not do this, instead do a more reliable momentary action on the relays via ESP32 code.
Plus later I'd like to do things like run code to override/toggle Zones using a temperature sensor on the ESP32
Reading the status of these INPUT pin in code shouldn't be an issue.
But how would I do a coded HIGH or LOW on "executor1(F("switch1"), PIN_SWITCH_1" for example?

Ah! That is pretty easy to do with ST_Anything. Use the S_TimedRelay device instead of your EX_Switch devices. This way, the output will simply stay on for whatever duration you specify anb then automatically turn back off.

Note: These are classified as "Sensor" devices (long story :wink: ), instead of "Executor" devices, so you'll also need to modify the section of code where they are added to st::Everything.

//******************************************************************************************
//  File: S_TimedRelay.h
//  Authors: Dan G Ogorchock
//
//  Summary:  S_TimedRelay is a class which implements the SmartThings "Relay" device capability.  It features
//			  an automatic-turn-off time delay for a relay to emulate a button press.
//
//			  It inherits from the st::Sensor class and clones much from the st::Executor Class
//
//			  Create an instance of this class in your sketch's global variable section
//			  For Example:  st::S_TimedRelay sensor1(F("relaySwitch1"), PIN_RELAY, LOW, true, 1000, 0, 1, 0);
//
//			  st::S_TimedRelay() constructor requires the following arguments
//				- String &name - REQUIRED - the name of the object - must match the Groovy ST_Anything DeviceType tile name
//				- byte pinOutput - REQUIRED - the Arduino Pin to be used as a digital output
//				- bool startingState - REQUIRED - the value desired for the initial state of the switch.  LOW = "off", HIGH = "on"
//				- bool invertLogic - REQUIRED - determines whether the Arduino Digital Ouput should use inverted logic
//				- long onTime - REQUIRED - the number of milliseconds to keep the output on, DEFAULTS to 1000 milliseconds
//				- long offTime - OPTIONAL - the number of milliseconds to keep the output off, DEFAULTS to 0
//				- int numCycles - OPTIONAL - the number of times to repeat the on/off cycle, DEFAULTS to 1
// 				- byte finalState - OPTIONAL - leave in X state after finishing sequence 0 = off, 1 = on , Defaults to 0

This could get a little more complicated. I would need to understand the logic you'd like to implement before recommending a solution within the ST_Anything framework.

Thanks Dan.
Pivoted from the switches to the timedrelay and that worked.

I'm still interested if there was a way to have manually coded pins changes mirroring/synching back into the ST-Anything loops/updates

Sure. The one way that I have used in the past is as follows:

  1. Declare a global variable(s) in the sketch (i.e. before the setup() routine) as follows:
String strCommand;
st::S_TimedRelay* timedRelay1;
st::S_TimedRelay* timedRelay2;
st::S_TimedRelay* timedRelay3;
st::S_TimedRelay* timedRelay4
  1. Inside the setup() routine, after the st::Everything::addSensor and addExectuor section, add the following:
timedRelay1 = sensor5;  //First timedRelay Device
timedRelay2 = sensor6;  //Second timedRelay Device
timedRelay3 = sensor7;  //Third timedRelay Device
timedRelay4 = sensor8;  //Fourth timedRelay Device
  1. Inside the loop() routine, one needs to be VERY careful when making the following calls. Since the loop() routine is called thousands of times per second, one need to only make these calls when data has changed. Otherwise, the system will flood the network with calls to the Hubitat hub to update the corresponding devices. PROCEED WITH EXTREME CAUTION!

Example:

strCommand = "switch1 on";
sensor5->beSmart(strCommand);

strCommand = "switch2 off";
sensor6->beSmart(strCommand);

strCommand = "switch3 on";
sensor7->beSmart(strCommand);

strCommand = "switch4 off";
sensor8->beSmart(strCommand);

Note that the strCommand is formatted exactly the same as what the HE hub would send to the microcontroller. Pay close attention to make sure that string matches the name of the device used in the setup() routine when declaring the timedRelay device.

If you're a C/C++ programmer, this will probably all make sense to you. If not, then it can definitely be confusing. Let me know if you have any questions.

Also, I cannot stress enough to use caution to NOT issue the above Step#3 commands on every iteration through the loop() routine. Doing so will cause issues.

Also, if you want to see a working example of the above design, please take a look at the "ST_Anything_Relays_Buttons_ESP8266.ino" example sketch from the ST_Anything GitHub repo. It uses S_timedRelay devices, as well as momentary pushbutton inputs wired to GPIO pins to allow manual activation of the S_timedRelay devices.

Here is a copy of it, although you probably already have a copy of it on your computer. :wink:

//******************************************************************************************
// File: ST_Anything_Relays_Butttons_ESP8266.ino
// Authors: Dan G Ogorchock & Daniel J Ogorchock (Father and Son)
//
// Summary: This Arduino Sketch, along with the ST_Anything library and the revised SmartThings
//          library, demonstrates the ability of one NodeMCU ESP8266 to
//          implement a multi input/output custom device for integration into SmartThings.
//
//          The ST_Anything library takes care of all of the work to schedule device updates
//          as well as all communications with the NodeMCU ESP8266’s WiFi.
//
//          ST_Anything_Relays_Buttons_ESP8266 implements the following ST Capabilities as a demo 
//          of what is possible with a single NodeMCU ESP8266
//            - 3 x Switch devices
//
//
// Change History:
//
//    Date        Who            What
//    ----        ---            ----
//    2015-01-03  Dan & Daniel   Original Creation
//    2017-02-12  Dan Ogorchock  Revised to use the new SMartThings v2.0 library
//    2017-04-17  Dan Ogorchock  New example showing use of Multiple device of same ST Capability
//                               used with new Parent/Child Device Handlers (i.e. Composite DH)
//    2017-05-25  Dan Ogorchock  Revised example sketch, taking into account limitations of NodeMCU GPIO pins
//    2017-11-29  Dan Ogorchock  New example to showcase local control using momentary buttons
//    2018-02-09  Dan Ogorchock  Added support for Hubitat Elevation Hub
//
//******************************************************************************************
//******************************************************************************************
// SmartThings Library for ESP8266WiFi
//******************************************************************************************
#include <SmartThingsESP8266WiFi.h>

//******************************************************************************************
// ST_Anything Library
//******************************************************************************************
#include <Constants.h> //Constants.h is designed to be modified by the end user to adjust behavior of the ST_Anything library
#include <Device.h> //Generic Device Class, inherited by Sensor and Executor classes
#include <Sensor.h> //Generic Sensor Class, typically provides data to ST Cloud (e.g. Temperature, Motion, etc…)
#include <Executor.h> //Generic Executor Class, typically receives data from ST Cloud (e.g. Switch)
#include <InterruptSensor.h> //Generic Interrupt "Sensor" Class, waits for change of state on digital input
#include <PollingSensor.h> //Generic Polling "Sensor" Class, polls Arduino pins periodically
#include <Everything.h> //Master Brain of ST_Anything library that ties everything together and performs ST Shield communications

#include <PS_Illuminance.h> //Implements a Polling Sensor (PS) to measure light levels via a photo resistor

#include <PS_TemperatureHumidity.h> //Implements a Polling Sensor (PS) to measure Temperature and Humidity via DHT library
#include <PS_DS18B20_Temperature.h> //Implements a Polling Sesnor (PS) to measure Temperature via DS18B20 libraries
#include <PS_Water.h> //Implements a Polling Sensor (PS) to measure presence of water (i.e. leak detector)
#include <IS_Motion.h> //Implements an Interrupt Sensor (IS) to detect motion via a PIR sensor
#include <IS_Contact.h> //Implements an Interrupt Sensor (IS) to monitor the status of a digital input pin
#include <IS_Smoke.h> //Implements an Interrupt Sensor (IS) to monitor the status of a digital input pin
#include <IS_DoorControl.h> //Implements an Interrupt Sensor (IS) and Executor to monitor the status of a digital input pin and control a digital output pin
#include <IS_Button.h> //Implements an Interrupt Sensor (IS) to monitor the status of a digital input pin for button presses
#include <EX_Switch.h> //Implements an Executor (EX) via a digital output to a relay
#include <EX_Alarm.h> //Implements Executor (EX)as an Alarm Siren capability via a digital output to a relay
#include <S_TimedRelay.h> //Implements a Sensor to control a digital output pin with timing capabilities

//*************************************************************************************************
//NodeMCU v1.0 ESP8266-12e Pin Definitions (makes it much easier as these match the board markings)
//*************************************************************************************************
//#define LED_BUILTIN 16
//#define BUILTIN_LED 16
//
//#define D0 16  //no internal pullup resistor
//#define D1  5
//#define D2  4
//#define D3  0  //must not be pulled low during power on/reset, toggles value during boot
//#define D4  2  //must not be pulled low during power on/reset, toggles value during boot
//#define D5 14
//#define D6 12
//#define D7 13
//#define D8 15  //must not be pulled high during power on/reset

//******************************************************************************************
//Define which Arduino Pins will be used for each device
//******************************************************************************************

#define PIN_RELAY_1 D5 //SmartThings Capability "Relay Switch"
#define PIN_RELAY_2 D6 //SmartThings Capability "Relay Switch"
#define PIN_RELAY_3 D7 //SmartThings Capability "Relay Switch"

//---Begin Push Button declarations---
#define MAX_PUSHBUTTONS 3
#define MIN_DEBOUNCE_TIME 50 //push-buttons must be held for 50ms to prevent chattering input

#define PIN_BUTTON1   D1
#define PIN_BUTTON2   D2
#define PIN_BUTTON3   D3

byte nBtnIndex;      //Index Variable
bool nCurrentVal;     //temp variable
String strCommand; 
byte nBtnVals[MAX_PUSHBUTTONS][2];   //Array of current[0] and last[1] values of the pushbuttons
byte nBtnPins[MAX_PUSHBUTTONS] = {PIN_BUTTON1, PIN_BUTTON2, PIN_BUTTON3};
unsigned long lngBtnLastMillis[MAX_PUSHBUTTONS]; //needed to properly debounce the pushbutton inputs
st::EX_Switch* swArray[MAX_PUSHBUTTONS]; //need an array of the executors so we can togle the correct one
//---End Push Button declarations--- 


//******************************************************************************************
//ESP8266 WiFi Information
//******************************************************************************************
String str_ssid     = "yourSSIDhere";                           //  <---You must edit this line!
String str_password = "yourWiFiPasswordhere";                   //  <---You must edit this line!
IPAddress ip(192, 168, 1, 227);       //Device IP Address       //  <---You must edit this line!
IPAddress gateway(192, 168, 1, 1);    //Router gateway          //  <---You must edit this line!
IPAddress subnet(255, 255, 255, 0);   //LAN subnet mask         //  <---You must edit this line!
IPAddress dnsserver(192, 168, 1, 1);  //DNS server              //  <---You must edit this line!
const unsigned int serverPort = 8090; // port to run the http server on

// Smartthings / Hubitat Hub TCP/IP Address
IPAddress hubIp(192, 168, 1, 149);    // smartthings/hubitat hub ip //  <---You must edit this line!

// SmartThings / Hubitat Hub TCP/IP Address: UNCOMMENT line that corresponds to your hub, COMMENT the other
const unsigned int hubPort = 39500;   // smartthings hub port
//const unsigned int hubPort = 39501;   // hubitat hub port

//******************************************************************************************
//st::Everything::callOnMsgSend() optional callback routine.  This is a sniffer to monitor 
//    data being sent to ST.  This allows a user to act on data changes locally within the 
//    Arduino sktech.
//******************************************************************************************
void callback(const String &msg)
{
//  Serial.print(F("ST_Anything Callback: Sniffed data = "));
//  Serial.println(msg);
  
  //TODO:  Add local logic here to take action when a device's value/state is changed
  
  //Masquerade as the ThingShield to send data to the Arduino, as if from the ST Cloud (uncomment and edit following line)
  //st::receiveSmartString("Put your command here!");  //use same strings that the Device Handler would send
}

//******************************************************************************************
//Arduino Setup() routine
//******************************************************************************************
void setup()
{
  //******************************************************************************************
  //Declare each Device that is attached to the Arduino
  //  Notes: - For each device, there is typically a corresponding "tile" defined in your 
  //           SmartThings Device Hanlder Groovy code, except when using new COMPOSITE Device Handler
  //         - For details on each device's constructor arguments below, please refer to the 
  //           corresponding header (.h) and program (.cpp) files.
  //         - The name assigned to each device (1st argument below) must match the Groovy
  //           Device Handler names.  (Note: "temphumid" below is the exception to this rule
  //           as the DHT sensors produce both "temperature" and "humidity".  Data from that
  //           particular sensor is sent to the ST Hub in two separate updates, one for 
  //           "temperature" and one for "humidity")
  //         - The new Composite Device Handler is comprised of a Parent DH and various Child
  //           DH's.  The names used below MUST not be changed for the Automatic Creation of
  //           child devices to work properly.  Simply increment the number by +1 for each duplicate
  //           device (e.g. contact1, contact2, contact3, etc...)  You can rename the Child Devices
  //           to match your specific use case in the ST Phone Application.
  //******************************************************************************************
  //Polling Sensors
  
  //Special sensors/executors (uses portions of both polling and executor classes)

  //EX_Switch arguments(name, pin, starting state,  invert logic) change last 2 args as needed for your application 
  static st::EX_Switch executor1(F("switch1"), PIN_RELAY_1, LOW, true);
  static st::EX_Switch executor2(F("switch2"), PIN_RELAY_2, LOW, true);
  static st::EX_Switch executor3(F("switch3"), PIN_RELAY_3, LOW, true);


  //*****************************************************************************
  //  Configure debug print output from each main class 
  //  -Note: Set these to "false" if using Hardware Serial on pins 0 & 1
  //         to prevent communication conflicts with the ST Shield communications
  //*****************************************************************************
  st::Everything::debug=true;
  st::Executor::debug=true;
  st::Device::debug=true;
  st::PollingSensor::debug=true;
  st::InterruptSensor::debug=true;

  //*****************************************************************************
  //Initialize the "Everything" Class
  //*****************************************************************************

  //Initialize the optional local callback routine (safe to comment out if not desired)
  st::Everything::callOnMsgSend = callback;
  
  //Create the SmartThings ESP8266WiFi Communications Object
    //STATIC IP Assignment - Recommended
    st::Everything::SmartThing = new st::SmartThingsESP8266WiFi(str_ssid, str_password, ip, gateway, subnet, dnsserver, serverPort, hubIp, hubPort, st::receiveSmartString);
 
    //DHCP IP Assigment - Must set your router's DHCP server to provice a static IP address for this device's MAC address
    //st::Everything::SmartThing = new st::SmartThingsESP8266WiFi(str_ssid, str_password, serverPort, hubIp, hubPort, st::receiveSmartString);

  //Run the Everything class' init() routine which establishes WiFi communications with SmartThings Hub
  st::Everything::init();
  
  //*****************************************************************************
  //Add each sensor to the "Everything" Class
  //*****************************************************************************

      
  //*****************************************************************************
  //Add each executor to the "Everything" Class
  //*****************************************************************************
  st::Everything::addExecutor(&executor1);
  st::Everything::addExecutor(&executor2);
  st::Everything::addExecutor(&executor3);
      
  //*****************************************************************************
  //Initialize each of the devices which were added to the Everything Class
  //*****************************************************************************
  st::Everything::initDevices();

  //*****************************************************************************
  //Add User Customized Setup Code Here (instead of modifying standard library files)
  //*****************************************************************************
  //---Begin Push Button initialization section---
  swArray[0]=&executor1;
  swArray[1]=&executor2;
  swArray[2]=&executor3;  

  //Allocate strCommand buffer one time to prevent Heap Fragmentation.
  strCommand.reserve(20);

  //Configure input pins for hardwired pusbuttons AND read initial values
  for (nBtnIndex=0; nBtnIndex < MAX_PUSHBUTTONS; nBtnIndex++) {
    pinMode(nBtnPins[nBtnIndex], INPUT_PULLUP);
    nBtnVals[nBtnIndex][0] = digitalRead(nBtnPins[nBtnIndex]);   // read the input pin
    nBtnVals[nBtnIndex][1] = nBtnVals[nBtnIndex][0];
    lngBtnLastMillis[nBtnIndex] = 0;  //initialize times to zero
  }
 //---End Push Button initialization section---
}

//******************************************************************************************
//Arduino Loop() routine
//******************************************************************************************
void loop()
{
  //*****************************************************************************
  //Execute the Everything run method which takes care of "Everything"
  //*****************************************************************************
  st::Everything::run();

  //*****************************************************************************
  //Add User Customized Loop Code Here (instead of modifying standard library files)
  //*****************************************************************************
  
  //---Begin Push Button execution section---
  //Loop through the pushbutton array
  for (nBtnIndex=0; nBtnIndex < MAX_PUSHBUTTONS; nBtnIndex++) 
  {
    nCurrentVal = digitalRead(nBtnPins[nBtnIndex]); // read the input pin   
    if (nCurrentVal != nBtnVals[nBtnIndex][1])      // only act if the button changed state
    {
      lngBtnLastMillis[nBtnIndex] = millis();  //keep track of when the button changed state
    }
    if ((millis() - lngBtnLastMillis[nBtnIndex] >= MIN_DEBOUNCE_TIME) && (nBtnVals[nBtnIndex][0] != nCurrentVal))
    {
      nBtnVals[nBtnIndex][0] = nCurrentVal; //keep current value for proper debounce logic
      if (nCurrentVal == LOW)    //only care if the button is pressed (change LOW to HIGH if logic reversed)
      {
        strCommand = swArray[nBtnIndex]->getName() + " " + (swArray[nBtnIndex]->getStatus()== HIGH?"off":"on");
        Serial.print(F("Pushbutton: "));
        Serial.println(strCommand);
        swArray[nBtnIndex]->beSmart(strCommand);  //Call the beSmart function of the proper executor object to either turn on or off the relay
        strCommand.remove(0); //clear the strCommand buffer
      }
    }
    nBtnVals[nBtnIndex][1] = nCurrentVal;  //keep last value for proper debounce logic
  }  
 //---End Push Button execution section---
}

Hope this helps!

And with example code! Nice :sunglasses:
Thanks very much Dan, very interesting, and helpful.
I'll give this a try-out soon.

1 Like

Was thinking that my OTHER Hubitat devices were acting a little odd of lately ...
Found LOTS of this in the Parent device LOG ... assuming this isnt normal?

dev:2812026-02-22 05:23:56.987 PMerrorcom.hubitat.app.exception.LimitExceededException: Device 281 generates excessive hub load on line 111 (method parse)
dev:2812026-02-22 05:23:53.990 PMerrorcom.hubitat.app.exception.LimitExceededException: Device 281 generates excessive hub load on line 111 (method parse)
dev:2812026-02-22 05:23:53.390 PMerrorcom.hubitat.app.exception.LimitExceededException: Device 281 generates excessive hub load on line 111 (method parse)
dev:2812026-02-22 05:23:52.370 PMerrorcom.hubitat.app.exception.LimitExceededException: Device 281 generates excessive hub load on line 111 (method parse)
dev:2812026-02-22 05:23:51.798 PMerrorcom.hubitat.app.exception.LimitExceededException: Device 281 generates excessive hub load on line 111 (method parse)
dev:2812026-02-22 05:23:50.562 PMerrorcom.hubitat.app.exception.LimitExceededException: Device 281 generates excessive hub load on line 111 (method parse)
dev:2812026-02-22 05:23:49.994 PMerrorcom.hubitat.app.exception.LimitExceededException: Device 281 generates excessive hub load on line 111 (method parse)

Not normal at all. I would need to see a copy of your Arduino sketch to look for clues as to why you’re seeing those warnings.

Edit ...
Added the missing "Delay(1000);"
No change.
Rebooted my Hubitat
Error gone

Might have been the Hubitat was overdue for a reboot
Apologies

Please share the code. Adding any “delay()” statements is NOT advised, as that will completely mess up the microcontroller timing.

Simply copy and paste your sketch, redacting your WiFi credentials, as preformatted text here in the forum. I really want to help make sure your code isn’t going to cause you issues going forward.

You are awesome!
The Hubitat Apps & Drivers are Brilliant!!
The help you give, Outstand!!!
Thank You from one HE Nerd to another HE Nerd!!!! :+1: