From fc76db092d97dd72c471acf7366b9dd445c4ca0c Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 27 Aug 2022 17:33:08 -0500 Subject: [PATCH 01/75] started development of Blinkable Interface to allow generic LED to be used with Blinker --- src/HAP.cpp | 7 +- src/HomeSpan.cpp | 95 +++++++----- src/HomeSpan.h | 12 +- src/Network.cpp | 26 +++- src/Settings.h | 2 - src/Utils.cpp | 322 +++++++++++++++++++---------------------- src/Utils.h | 184 ++++++++++++----------- src/extras/Blinker.cpp | 176 ++++++++++++++++++++++ src/extras/Blinker.h | 146 +++++++++++++++++++ src/extras/Pixel.h | 3 +- src/src.ino | 10 +- 11 files changed, 669 insertions(+), 314 deletions(-) create mode 100644 src/extras/Blinker.cpp create mode 100644 src/extras/Blinker.h diff --git a/src/HAP.cpp b/src/HAP.cpp index c4b2844..a44b9ee 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -637,7 +637,9 @@ int HAPClient::postPairSetupURL(){ mdns_service_txt_item_set("_hap","_tcp","sf","0"); // broadcast new status LOG1("\n*** ACCESSORY PAIRED! ***\n"); - homeSpan.statusLED.on(); + + if(homeSpan.statusLED) + homeSpan.statusLED->on(); if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(true); @@ -1636,7 +1638,8 @@ void HAPClient::removeController(uint8_t *id){ removeControllers(); LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - homeSpan.statusLED.start(LED_PAIRING_NEEDED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_PAIRING_NEEDED); if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(false); } diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index bba3091..b59b237 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -59,8 +59,6 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); // required to avoid watchdog timeout messages from ESP32-C3 - statusLED.init(statusPin,0,autoOffLED); - if(requestedMaxCon=0){ - Serial.print(statusPin); + if(getStatusPin()>=0){ + Serial.print(getStatusPin()); if(autoOffLED>0) Serial.printf(" (Auto Off=%d sec)",autoOffLED); } @@ -189,10 +187,11 @@ void Span::pollTask() { processSerialCommand("A"); } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); - statusLED.start(LED_WIFI_NEEDED); + if(statusLED) + statusLED->start(LED_WIFI_NEEDED); } - } else { - homeSpan.statusLED.start(LED_WIFI_CONNECTING); + } else if(statusLED) { + statusLED->start(LED_WIFI_CONNECTING); } if(controlButton) @@ -290,12 +289,12 @@ void Span::pollTask() { if(spanOTA.enabled) ArduinoOTA.handle(); - if(controlButton && controlButton->primed()){ - statusLED.start(LED_ALERT); - } + if(controlButton && controlButton->primed() && statusLED) + statusLED->start(LED_ALERT); if(controlButton && controlButton->triggered(3000,10000)){ - statusLED.off(); + if(statusLED) + statusLED->off(); if(controlButton->type()==PushButton::LONG){ controlButton->wait(); processSerialCommand("F"); // FACTORY RESET @@ -304,7 +303,8 @@ void Span::pollTask() { } } - statusLED.check(); + if(statusLED) + statusLED->check(); } // poll @@ -323,11 +323,16 @@ int Span::getFreeSlot(){ ////////////////////////////////////// void Span::commandMode(){ + + if(!statusLED){ + Serial.print("*** ERROR: CAN'T ENTER COMMAND MODE WITHOUT A DEFINED STATUS LED***\n\n"); + return; + } Serial.print("*** ENTERING COMMAND MODE ***\n\n"); int mode=1; boolean done=false; - statusLED.start(500,0.3,mode,1000); + statusLED->start(500,0.3,mode,1000); unsigned long alarmTime=millis()+comModeLife; @@ -338,7 +343,7 @@ void Span::commandMode(){ Serial.print(" seconds).\n\n"); mode=1; done=true; - statusLED.start(LED_ALERT); + statusLED->start(LED_ALERT); delay(2000); } else if(controlButton->triggered(10,3000)){ @@ -346,14 +351,14 @@ void Span::commandMode(){ mode++; if(mode==6) mode=1; - statusLED.start(500,0.3,mode,1000); + statusLED->start(500,0.3,mode,1000); } else { done=true; } } // button press } // while - statusLED.start(LED_ALERT); + statusLED->start(LED_ALERT); controlButton->wait(); switch(mode){ @@ -361,12 +366,12 @@ void Span::commandMode(){ case 1: Serial.print("*** NO ACTION\n\n"); if(strlen(network.wifiData.ssid)==0) - statusLED.start(LED_WIFI_NEEDED); + statusLED->start(LED_WIFI_NEEDED); else if(!HAPClient::nAdminControllers()) - statusLED.start(LED_PAIRING_NEEDED); + statusLED->start(LED_PAIRING_NEEDED); else - statusLED.on(); + statusLED->on(); break; case 2: @@ -402,7 +407,8 @@ void Span::checkConnect(){ connected++; waitTime=60000; alarmConnect=0; - homeSpan.statusLED.start(LED_WIFI_CONNECTING); + if(statusLED) + statusLED->start(LED_WIFI_CONNECTING); } if(WiFi.status()!=WL_CONNECTED){ @@ -429,10 +435,12 @@ void Span::checkConnect(){ return; } - if(!HAPClient::nAdminControllers()) - statusLED.start(LED_PAIRING_NEEDED); - else - statusLED.on(); + if(statusLED){ + if(!HAPClient::nAdminControllers()) + statusLED->start(LED_PAIRING_NEEDED); + else + statusLED->on(); + } connected++; @@ -756,11 +764,13 @@ void Span::processSerialCommand(const char *c){ Serial.print("\nDEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - - if(strlen(network.wifiData.ssid)==0) - statusLED.start(LED_WIFI_NEEDED); - else - statusLED.start(LED_PAIRING_NEEDED); + + if(statusLED){ + if(strlen(network.wifiData.ssid)==0) + statusLED->start(LED_WIFI_NEEDED); + else + statusLED->start(LED_PAIRING_NEEDED); + } } break; @@ -777,7 +787,8 @@ void Span::processSerialCommand(const char *c){ nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data nvs_commit(wifiNVS); // commit to NVS Serial.print("\n*** WiFi Credentials SAVED! Re-starting ***\n\n"); - statusLED.off(); + if(statusLED) + statusLED->off(); delay(1000); ESP.restart(); } @@ -810,7 +821,8 @@ void Span::processSerialCommand(const char *c){ } Serial.print("\n*** Re-starting ***\n\n"); - statusLED.off(); + if(statusLED) + statusLED->off(); delay(1000); ESP.restart(); // re-start device } @@ -818,7 +830,8 @@ void Span::processSerialCommand(const char *c){ case 'X': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_erase_all(wifiNVS); nvs_commit(wifiNVS); Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); @@ -837,7 +850,8 @@ void Span::processSerialCommand(const char *c){ case 'H': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); Serial.print("\n*** HomeSpan Device ID and Pairing Data DELETED! Restarting...\n\n"); @@ -848,7 +862,8 @@ void Span::processSerialCommand(const char *c){ case 'R': { - statusLED.off(); + if(statusLED) + statusLED->off(); Serial.print("\n*** Restarting...\n\n"); delay(1000); ESP.restart(); @@ -857,7 +872,8 @@ void Span::processSerialCommand(const char *c){ case 'F': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); nvs_erase_all(wifiNVS); @@ -874,7 +890,8 @@ void Span::processSerialCommand(const char *c){ case 'E': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_flash_erase(); Serial.print("\n*** ALL DATA ERASED! Restarting...\n\n"); delay(1000); @@ -2098,7 +2115,8 @@ void SpanOTA::start(){ Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); otaPercent=0; - homeSpan.statusLED.start(LED_OTA_STARTED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_OTA_STARTED); } /////////////////////////////// @@ -2107,7 +2125,8 @@ void SpanOTA::end(){ nvs_set_u8(homeSpan.otaNVS,"OTA_REQUIRED",safeLoad); nvs_commit(homeSpan.otaNVS); Serial.printf(" DONE! Rebooting...\n"); - homeSpan.statusLED.off(); + if(homeSpan.statusLED) + homeSpan.statusLED->off(); delay(100); // make sure commit it finished before reboot } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 3bac4d5..dced380 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -40,6 +40,7 @@ #include #include +#include "extras/Blinker.h" #include "Settings.h" #include "Utils.h" #include "Network.h" @@ -189,7 +190,6 @@ class Span{ unsigned long alarmConnect=0; // time after which WiFi connection attempt should be tried again const char *defaultSetupCode=DEFAULT_SETUP_CODE; // Setup Code used for pairing - int statusPin=DEFAULT_STATUS_PIN; // pin for Status LED uint16_t autoOffLED=0; // automatic turn-off duration (in seconds) for Status LED uint8_t logLevel=DEFAULT_LOG_LEVEL; // level for writing out log messages to serial monitor uint8_t maxConnections=CONFIG_LWIP_MAX_SOCKETS-2; // maximum number of allowed simultaneous HAP connections @@ -203,7 +203,7 @@ class Span{ void (*apFunction)()=NULL; // optional function to invoke when starting Access Point WiFiServer *hapServer; // pointer to the HAP Server connection - Blinker statusLED; // indicates HomeSpan status + Blinker *statusLED = NULL; // indicates HomeSpan status PushButton *controlButton = NULL; // controls HomeSpan configuration and resets Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point SpanWebLog webLog; // optional web status/log @@ -254,10 +254,12 @@ class Span{ boolean updateDatabase(boolean updateMDNS=true); // updates HAP Configuration Number and Loop vector; if updateMDNS=true and config number has changed, re-broadcasts MDNS 'c#' record; returns true if config number changed boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false - void setControlPin(uint8_t pin){controlButton=new PushButton(pin);} // sets Control Pin - void setStatusPin(uint8_t pin){statusPin=pin;} // sets Status Pin + void setControlPin(uint8_t pin){controlButton=new PushButton(pin);} // sets Control Pin + void setStatusPin(uint8_t pin){statusLED=new Blinker(new LED(pin),0,autoOffLED);} // sets Status Pin +// void setStatusPin(Blinkable *led){statusLED=new Blinker(led,0,autoOffLED);} // sets Status Blinkable LED + void setStatusAutoOff(uint16_t duration){autoOffLED=duration;} // sets Status LED auto off (seconds) - int getStatusPin(){return(statusPin);} // get Status Pin + int getStatusPin(){return(statusLED?statusLED->getPin():-1);} // get Status Pin (returns -1 if undefined) int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined) void setApSSID(const char *ssid){network.apSSID=ssid;} // sets Access Point SSID void setApPassword(const char *pwd){network.apPassword=pwd;} // sets Access Point Password diff --git a/src/Network.cpp b/src/Network.cpp index cc1c102..47172cc 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -30,6 +30,8 @@ #include "Network.h" #include "HomeSpan.h" #include "Utils.h" +#include "extras/Blinker.h" + using namespace Utils; @@ -116,7 +118,8 @@ void Network::apConfigure(){ Serial.print(apPassword); Serial.print("\n"); - homeSpan.statusLED.start(LED_AP_STARTED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_AP_STARTED); Serial.print("\nScanning for Networks...\n\n"); @@ -154,10 +157,12 @@ void Network::apConfigure(){ if(homeSpan.controlButton && homeSpan.controlButton->triggered(9999,3000)){ Serial.print("\n*** Access Point Terminated."); - homeSpan.statusLED.start(LED_ALERT); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_ALERT); homeSpan.controlButton->wait(); Serial.print(" Restarting... \n\n"); - homeSpan.statusLED.off(); + if(homeSpan.statusLED) + homeSpan.statusLED->off(); ESP.restart(); } @@ -176,9 +181,11 @@ void Network::apConfigure(){ Serial.print("\n*** Access Point: Configuration Canceled."); } Serial.print(" Restarting...\n\n"); - homeSpan.statusLED.start(LED_ALERT); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_ALERT); delay(1000); - homeSpan.statusLED.off(); + if(homeSpan.statusLED) + homeSpan.statusLED->off(); ESP.restart(); } } @@ -273,7 +280,8 @@ void Network::processRequest(char *body, char *formData){ getFormValue(formData,"network",wifiData.ssid,MAX_SSID); getFormValue(formData,"pwd",wifiData.pwd,MAX_PWD); - homeSpan.statusLED.start(LED_WIFI_CONNECTING); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_WIFI_CONNECTING); responseBody+="" "

Initiating WiFi connection to:

" + String(wifiData.ssid) + "

"; @@ -320,7 +328,8 @@ void Network::processRequest(char *body, char *formData){ } else { - homeSpan.statusLED.start(LED_AP_CONNECTED); // slow double-blink + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_AP_CONNECTED); // slow double-blink responseBody+="

SUCCESS! Connected to:

" + String(wifiData.ssid) + "

"; responseBody+="

You may enter new 8-digit Setup Code below, or leave blank to retain existing code.

"; @@ -340,7 +349,8 @@ void Network::processRequest(char *body, char *formData){ LOG1("In Landing Page...\n"); - homeSpan.statusLED.start(LED_AP_CONNECTED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_AP_CONNECTED); waitTime=2; responseBody+="

Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.

" diff --git a/src/Settings.h b/src/Settings.h index 73225ad..7ef9de3 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -71,8 +71,6 @@ #define DEFAULT_QR_ID "HSPN" // change with homeSpan.setQRID(qrID); -#define DEFAULT_STATUS_PIN -1 // change with homeSpan.setStatusPin(pin) - #define DEFAULT_AP_SSID "HomeSpan-Setup" // change with homeSpan.setApSSID(ssid) #define DEFAULT_AP_PASSWORD "homespan" // change with homeSpan.setApPassword(pwd) #define DEFAULT_OTA_PASSWORD "homespan-ota" // change with 'O' command diff --git a/src/Utils.cpp b/src/Utils.cpp index 5997e28..31b326d 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -227,178 +227,150 @@ void PushButton::reset(){ touch_value_t PushButton::threshold=0; #endif -//////////////////////////////// -// Blinker // -//////////////////////////////// - -Blinker::Blinker(){ -} - -////////////////////////////////////// - -Blinker::Blinker(int pin, int timerNum, uint16_t autoOffDuration){ - init(pin, timerNum, autoOffDuration); -} - -////////////////////////////////////// - -void Blinker::init(int pin, int timerNum, uint16_t autoOffDuration){ - - this->pin=pin; - if(pin<0) - return; - - pinMode(pin,OUTPUT); - digitalWrite(pin,0); - - pauseDuration=autoOffDuration*1000; - -#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group - group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; - idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group -#else - group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; - idx=TIMER_0; -#endif - - timer_config_t conf; - conf.alarm_en=TIMER_ALARM_EN; - conf.counter_en=TIMER_PAUSE; - conf.intr_type=TIMER_INTR_LEVEL; - conf.counter_dir=TIMER_COUNT_UP; - conf.auto_reload=TIMER_AUTORELOAD_EN; - conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) - -#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure - conf.clk_src=TIMER_SRC_CLK_APB; -#endif - - timer_init(group,idx,&conf); - timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); - timer_enable_intr(group,idx); - -} - -////////////////////////////////////// - -void Blinker::isrTimer(void *arg){ - - Blinker *b=(Blinker *)arg; - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 - timer_group_clr_intr_status_in_isr(b->group,b->idx); -#else // use older method that is only for ESP32 - if(b->group){ - if(b->idx) - TIMERG1.int_clr_timers.t1=1; - else - TIMERG1.int_clr_timers.t0=1; - } else { - if(b->idx) - TIMERG0.int_clr_timers.t1=1; - else - TIMERG0.int_clr_timers.t0=1; - } -#endif - - if(!digitalRead(b->pin)){ - digitalWrite(b->pin,1); - timer_set_alarm_value(b->group,b->idx,b->onTime); - b->count--; - } else { - digitalWrite(b->pin,0); - if(b->count){ - timer_set_alarm_value(b->group,b->idx,b->offTime); - } else { - timer_set_alarm_value(b->group,b->idx,b->delayTime); - b->count=b->nBlinks; - } - } - - timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); - -} - -////////////////////////////////////// - -void Blinker::start(int period, float dutyCycle){ - - start(period, dutyCycle, 1, 0); -} - -////////////////////////////////////// - -void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ - - if(pin<0) - return; - - pauseTime=millis(); - isPaused=false; - gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); // needed to ensure digitalRead() functions correctly on ESP32-C3; also needed to re-enable after pause() - - period*=10; - onTime=dutyCycle*period; - offTime=period-onTime; - this->delayTime=delayTime*10+offTime; - this->nBlinks=nBlinks; - count=nBlinks; - timer_set_counter_value(group,idx,0); - timer_set_alarm_value(group,idx,0); - timer_start(group,idx); -} - -////////////////////////////////////// - -void Blinker::stop(){ - - if(pin<0) - return; - - timer_pause(group,idx); -} - -////////////////////////////////////// - -void Blinker::on(){ - - if(pin<0) - return; - - pauseTime=millis(); - isPaused=false; - gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); - - stop(); - digitalWrite(pin,1); -} - -////////////////////////////////////// - -void Blinker::off(){ - - if(pin<0) - return; - - pauseTime=millis(); - isPaused=false; - gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); - - stop(); - digitalWrite(pin,0); -} - -////////////////////////////////////// - -void Blinker::check(){ - - if(pin<0) - return; - - if(pauseDuration==0 || isPaused || (millis()-pauseTime)led=led; +// +// pauseDuration=autoOffDuration*1000; +// +//#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group +// group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; +// idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group +//#else +// group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; +// idx=TIMER_0; +//#endif +// +// timer_config_t conf; +// conf.alarm_en=TIMER_ALARM_EN; +// conf.counter_en=TIMER_PAUSE; +// conf.intr_type=TIMER_INTR_LEVEL; +// conf.counter_dir=TIMER_COUNT_UP; +// conf.auto_reload=TIMER_AUTORELOAD_EN; +// conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) +// +//#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure +// conf.clk_src=TIMER_SRC_CLK_APB; +//#endif +// +// timer_init(group,idx,&conf); +// timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); +// timer_enable_intr(group,idx); +// +//} +// +//////////////////////////////////////// +// +//void Blinker::isrTimer(void *arg){ +// +// Blinker *b=(Blinker *)arg; +// +//#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 +// timer_group_clr_intr_status_in_isr(b->group,b->idx); +//#else // use older method that is only for ESP32 +// if(b->group){ +// if(b->idx) +// TIMERG1.int_clr_timers.t1=1; +// else +// TIMERG1.int_clr_timers.t0=1; +// } else { +// if(b->idx) +// TIMERG0.int_clr_timers.t1=1; +// else +// TIMERG0.int_clr_timers.t0=1; +// } +//#endif +// +// if(!(b->led->isOn())){ +// b->led->on(); +// timer_set_alarm_value(b->group,b->idx,b->onTime); +// b->count--; +// } else { +// b->led->off(); +// if(b->count){ +// timer_set_alarm_value(b->group,b->idx,b->offTime); +// } else { +// timer_set_alarm_value(b->group,b->idx,b->delayTime); +// b->count=b->nBlinks; +// } +// } +// +// timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); +// +//} +// +//////////////////////////////////////// +// +//void Blinker::start(int period, float dutyCycle){ +// +// start(period, dutyCycle, 1, 0); +//} +// +//////////////////////////////////////// +// +//void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ +// +// pauseTime=millis(); +// isPaused=false; +// +// period*=10; +// onTime=dutyCycle*period; +// offTime=period-onTime; +// this->delayTime=delayTime*10+offTime; +// this->nBlinks=nBlinks; +// count=nBlinks; +// timer_set_counter_value(group,idx,0); +// timer_set_alarm_value(group,idx,0); +// timer_start(group,idx); +//} +// +//////////////////////////////////////// +// +//void Blinker::stop(){ +// +// timer_pause(group,idx); +//} +// +//////////////////////////////////////// +// +//void Blinker::on(){ +// +// pauseTime=millis(); +// isPaused=false; +// +// stop(); +// led->on(); +//} +// +//////////////////////////////////////// +// +//void Blinker::off(){ +// +// pauseTime=millis(); +// isPaused=false; +// +// stop(); +// led->off(); +//} +// +//////////////////////////////////////// +// +//void Blinker::check(){ +// +// if(pauseDuration==0 || isPaused || (millis()-pauseTime)off(); +//} +// +//////////////////////////////////////// +// +//int Blinker::getPin(){ +// return(led->getPin()); +//} diff --git a/src/Utils.h b/src/Utils.h index b32922c..7af49ed 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -29,6 +29,7 @@ #include #include +#include "extras/Blinker.h" namespace Utils { @@ -37,6 +38,21 @@ String mask(char *c, int n); // simply utility that creates a String fr } +//////////////////////////////// +// Blinkable Interface // +//////////////////////////////// + +//class Blinkable { +// protected: +// boolean onState=false; +// +// public: +// virtual void on()=0; +// virtual void off()=0; +// virtual int getPin()=0; +// boolean isOn() {return(onState);} +//}; + ///////////////////////////////////////////////// // Creates a temporary buffer that is freed after // going out of scope @@ -182,98 +198,104 @@ class PushButton{ }; + + +////////////////////////////////// +//// Generic_LED // +////////////////////////////////// +// +//class LED : public Blinkable { +// int pin; +// +// public: +// +// LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} +// void on() {digitalWrite(pin,HIGH);onState=true;} +// void off() {digitalWrite(pin,LOW);onState=false;} +// int getPin() {return(pin);} +//}; + //////////////////////////////// // Blinker // //////////////////////////////// -class Blinker { - - timer_group_t group; - timer_idx_t idx; - int pin; - - int nBlinks; - int onTime; - int offTime; - int delayTime; - int count; - - unsigned long pauseDuration; - unsigned long pauseTime; - boolean isPaused=false; - - static void isrTimer(void *arg); - - public: - - Blinker(); - Blinker(int pin, int timerNum=0, uint16_t autoOffDuration=0); - -// Creates a generic blinking LED on specified pin controlled -// in background via interrupts generated by an ESP32 Alarm Timer. +//class Blinker { +// +// timer_group_t group; +// timer_idx_t idx; // -// In the first form, a Blinker is instantiated without specifying -// the pin. In this case the pin must be specified in a subsequent call -// to init() before the Blinker can be used. +// Blinkable *led; // -// In the second form, a Blinker is instantiated and initialized with -// the specified pin, obviating the need for a separate call to init(). +// int nBlinks; +// int onTime; +// int offTime; +// int delayTime; +// int count; +// +// unsigned long pauseDuration; +// unsigned long pauseTime; +// boolean isPaused=false; // -// pin: Pin mumber to control. Blinker will set pinMode to OUTPUT automatically +// static void isrTimer(void *arg); // -// timerNum: ESP32 Alarm Timer to use. -// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 -// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +// public: // -// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed -// Blinker will resume normal operation upon next call to start(), on(), or off() -// Program must periodically call check() for auto-off functionality to work - - void init(int pin, int timerNum=0, uint16_t autoOffDuration=0); - -// Initializes Blinker, if not configured during instantiation. +// Blinker(Blinkable *led, int timerNum=0, uint16_t autoOffDuration=0); // -// pin: Pin mumber to control. Blinker will set pinMode to OUTPUT automatically +//// Creates a generic blinking LED on specified pin controlled +//// in background via interrupts generated by an ESP32 Alarm Timer. +//// +//// In the first form, a Blinker is instantiated without specifying +//// the pin. In this case the pin must be specified in a subsequent call +//// to init() before the Blinker can be used. +//// +//// In the second form, a Blinker is instantiated and initialized with +//// the specified pin, obviating the need for a separate call to init(). +//// +//// led: An initialized LED device that implements the Blinkable Interface +//// +//// timerNum: ESP32 Alarm Timer to use. +//// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 +//// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +//// +//// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed +//// Blinker will resume normal operation upon next call to start(), on(), or off() +//// Program must periodically call check() for auto-off functionality to work +// +// void start(int period, float dutyCycle=0.5); +// +//// Starts simple ON/OFF blinking. +//// +//// period: ON/OFF blinking period, in milliseconds +//// dutyCycle: Fraction of period that LED is ON (default=50%) // -// timerNum: ESP32 Alarm Timer to use. -// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 -// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +// void start(int period, float dutyCycle, int nBlinks, int delayTime); // -// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed -// Blinker will resume normal operation upon next call to start(), on(), or off() -// Program must periodically call check() for auto-off functionality to work - - void start(int period, float dutyCycle=0.5); - -// Starts simple ON/OFF blinking. +//// Starts ON/OFF blinking pattern. +//// +//// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern +//// dutyCycle: Fraction of period that LED is ON (default=50%) +//// nBlinks: Number of blinks in blinking portion of pattern +//// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern // -// period: ON/OFF blinking period, in milliseconds -// dutyCycle: Fraction of period that LED is ON (default=50%) - - void start(int period, float dutyCycle, int nBlinks, int delayTime); - -// Starts ON/OFF blinking pattern. +// void stop(); // -// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern -// dutyCycle: Fraction of period that LED is ON (default=50%) -// nBlinks: Number of blinks in blinking portion of pattern -// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern - - void stop(); - -// Stops current blinking pattern. - - void on(); - -// Stops current blinking pattern and turns on LED - - void off(); - -// Stops current blinking pattern and turns off LED - - void check(); - -// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) - - -}; +//// Stops current blinking pattern. +// +// void on(); +// +//// Stops current blinking pattern and turns on LED +// +// void off(); +// +//// Stops current blinking pattern and turns off LED +// +// void check(); +// +//// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) +// +// int getPin(); +// +//// Returns pin number of connected LED +// +//}; diff --git a/src/extras/Blinker.cpp b/src/extras/Blinker.cpp new file mode 100644 index 0000000..dad0ca2 --- /dev/null +++ b/src/extras/Blinker.cpp @@ -0,0 +1,176 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#include "Blinker.h" + + //////////////////////////////// +// Blinker // +//////////////////////////////// + +Blinker::Blinker(Blinkable *led, int timerNum, uint16_t autoOffDuration){ + + this->led=led; + + pauseDuration=autoOffDuration*1000; + +#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group + group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; + idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group +#else + group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; + idx=TIMER_0; +#endif + + timer_config_t conf; + conf.alarm_en=TIMER_ALARM_EN; + conf.counter_en=TIMER_PAUSE; + conf.intr_type=TIMER_INTR_LEVEL; + conf.counter_dir=TIMER_COUNT_UP; + conf.auto_reload=TIMER_AUTORELOAD_EN; + conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) + +#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure + conf.clk_src=TIMER_SRC_CLK_APB; +#endif + + timer_init(group,idx,&conf); + timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); + timer_enable_intr(group,idx); + +} + +////////////////////////////////////// + +void Blinker::isrTimer(void *arg){ + + Blinker *b=(Blinker *)arg; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 + timer_group_clr_intr_status_in_isr(b->group,b->idx); +#else // use older method that is only for ESP32 + if(b->group){ + if(b->idx) + TIMERG1.int_clr_timers.t1=1; + else + TIMERG1.int_clr_timers.t0=1; + } else { + if(b->idx) + TIMERG0.int_clr_timers.t1=1; + else + TIMERG0.int_clr_timers.t0=1; + } +#endif + + if(!(b->led->isOn())){ + b->led->on(); + timer_set_alarm_value(b->group,b->idx,b->onTime); + b->count--; + } else { + b->led->off(); + if(b->count){ + timer_set_alarm_value(b->group,b->idx,b->offTime); + } else { + timer_set_alarm_value(b->group,b->idx,b->delayTime); + b->count=b->nBlinks; + } + } + + timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); + +} + +////////////////////////////////////// + +void Blinker::start(int period, float dutyCycle){ + + start(period, dutyCycle, 1, 0); +} + +////////////////////////////////////// + +void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ + + pauseTime=millis(); + isPaused=false; + + period*=10; + onTime=dutyCycle*period; + offTime=period-onTime; + this->delayTime=delayTime*10+offTime; + this->nBlinks=nBlinks; + count=nBlinks; + timer_set_counter_value(group,idx,0); + timer_set_alarm_value(group,idx,0); + timer_start(group,idx); +} + +////////////////////////////////////// + +void Blinker::stop(){ + + timer_pause(group,idx); +} + +////////////////////////////////////// + +void Blinker::on(){ + + pauseTime=millis(); + isPaused=false; + + stop(); + led->on(); +} + +////////////////////////////////////// + +void Blinker::off(){ + + pauseTime=millis(); + isPaused=false; + + stop(); + led->off(); +} + +////////////////////////////////////// + +void Blinker::check(){ + + if(pauseDuration==0 || isPaused || (millis()-pauseTime)off(); +} + +////////////////////////////////////// + +int Blinker::getPin(){ + return(led->getPin()); +} diff --git a/src/extras/Blinker.h b/src/extras/Blinker.h new file mode 100644 index 0000000..9794d6b --- /dev/null +++ b/src/extras/Blinker.h @@ -0,0 +1,146 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#pragma once + +#include +#include + +//////////////////////////////// +// Blinkable Interface // +//////////////////////////////// + +class Blinkable { + protected: + boolean onState=false; + + public: + virtual void on()=0; + virtual void off()=0; + virtual int getPin()=0; + boolean isOn() {return(onState);} +}; + +//////////////////////////////// +// Generic_LED // +//////////////////////////////// + +class LED : public Blinkable { + int pin; + + public: + + LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} + void on() {digitalWrite(pin,HIGH);onState=true;} + void off() {digitalWrite(pin,LOW);onState=false;} + int getPin() {return(pin);} +}; + +//////////////////////////////// +// Blinker // +//////////////////////////////// + +class Blinker { + + timer_group_t group; + timer_idx_t idx; + + Blinkable *led; + + int nBlinks; + int onTime; + int offTime; + int delayTime; + int count; + + unsigned long pauseDuration; + unsigned long pauseTime; + boolean isPaused=false; + + static void isrTimer(void *arg); + + public: + + Blinker(Blinkable *led, int timerNum=0, uint16_t autoOffDuration=0); + +// Creates a generic blinking LED on specified pin controlled +// in background via interrupts generated by an ESP32 Alarm Timer. +// +// In the first form, a Blinker is instantiated without specifying +// the pin. In this case the pin must be specified in a subsequent call +// to init() before the Blinker can be used. +// +// In the second form, a Blinker is instantiated and initialized with +// the specified pin, obviating the need for a separate call to init(). +// +// led: An initialized LED device that implements the Blinkable Interface +// +// timerNum: ESP32 Alarm Timer to use. +// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 +// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +// +// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed +// Blinker will resume normal operation upon next call to start(), on(), or off() +// Program must periodically call check() for auto-off functionality to work + + void start(int period, float dutyCycle=0.5); + +// Starts simple ON/OFF blinking. +// +// period: ON/OFF blinking period, in milliseconds +// dutyCycle: Fraction of period that LED is ON (default=50%) + + void start(int period, float dutyCycle, int nBlinks, int delayTime); + +// Starts ON/OFF blinking pattern. +// +// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern +// dutyCycle: Fraction of period that LED is ON (default=50%) +// nBlinks: Number of blinks in blinking portion of pattern +// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern + + void stop(); + +// Stops current blinking pattern. + + void on(); + +// Stops current blinking pattern and turns on LED + + void off(); + +// Stops current blinking pattern and turns off LED + + void check(); + +// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) + + int getPin(); + +// Returns pin number of connected LED + +}; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 79a7b5a..27d431c 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -33,12 +33,13 @@ #include "RFControl.h" #include "PwmPin.h" +#include "Blinker.h" //////////////////////////////////////////// // Single-Wire RGB/RGBW NeoPixels // //////////////////////////////////////////// -class Pixel { +class Pixel : Blinkable { public: struct Color { diff --git a/src/src.ino b/src/src.ino index 19e4ae3..6e0bc53 100644 --- a/src/src.ino +++ b/src/src.ino @@ -3,6 +3,9 @@ // as well as compile and test from this point. This file is ignored when the library is included in other sketches. #include "HomeSpan.h" +#include "FeatherPins.h" +#include "extras/Pixel.h" +#include "extras/Blinker.h" #define STRING_t const char * // WORK-AROUND @@ -14,8 +17,11 @@ void setup() { Serial.begin(115200); // homeSpan.setLogLevel(2); -// homeSpan.setStatusPin(13); - homeSpan.setControlPin(33); + homeSpan.setControlPin(F25); + homeSpan.setStatusPin(F26); +// homeSpan.setStatusPin(new LED(F26)); + +// new Pixel(F27); homeSpan.setHostNameSuffix("-lamp1"); homeSpan.setPortNum(1201); From 294b8d8d71453d32f22915940756c8d3818eeb11 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 28 Aug 2022 17:49:48 -0500 Subject: [PATCH 02/75] Complete re-write of Blinker Class Rather than use ESP32 timers, simply spawn a new task that turn on/off LED using simple delays. By avoiding the use of the ESP32 timer, we are no longer limited by the number of Blinkers possible. Also, Blinker relied on the timer interrupt, which crashed when it tried to call Pixel since Pixel in turn uses interrupts from the RMT. Switching to spawned tasks is much cleaner and does not consume more CPU time since the timer interrupts were driven by CPU anyway. Blinker class and generic LED class are now in extras.h. Blinkable Interface is also in extras.h. To Do: Incorporate new Blinker class into HomeSpan code --- src/extras/Blinker.cpp | 108 ++++++++++++----------------------------- src/extras/Blinker.h | 36 ++++---------- src/extras/Pixel.h | 5 +- src/extras/extras.ino | 42 ++++++---------- 4 files changed, 59 insertions(+), 132 deletions(-) diff --git a/src/extras/Blinker.cpp b/src/extras/Blinker.cpp index dad0ca2..9e65d48 100644 --- a/src/extras/Blinker.cpp +++ b/src/extras/Blinker.cpp @@ -31,76 +31,27 @@ // Blinker // //////////////////////////////// -Blinker::Blinker(Blinkable *led, int timerNum, uint16_t autoOffDuration){ - +Blinker::Blinker(Blinkable *led, uint16_t autoOffDuration){ this->led=led; - pauseDuration=autoOffDuration*1000; - -#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group - group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; - idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group -#else - group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; - idx=TIMER_0; -#endif - - timer_config_t conf; - conf.alarm_en=TIMER_ALARM_EN; - conf.counter_en=TIMER_PAUSE; - conf.intr_type=TIMER_INTR_LEVEL; - conf.counter_dir=TIMER_COUNT_UP; - conf.auto_reload=TIMER_AUTORELOAD_EN; - conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) - -#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure - conf.clk_src=TIMER_SRC_CLK_APB; -#endif - - timer_init(group,idx,&conf); - timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); - timer_enable_intr(group,idx); - } ////////////////////////////////////// -void Blinker::isrTimer(void *arg){ +void Blinker::blinkTask(void *arg){ Blinker *b=(Blinker *)arg; - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 - timer_group_clr_intr_status_in_isr(b->group,b->idx); -#else // use older method that is only for ESP32 - if(b->group){ - if(b->idx) - TIMERG1.int_clr_timers.t1=1; - else - TIMERG1.int_clr_timers.t0=1; - } else { - if(b->idx) - TIMERG0.int_clr_timers.t1=1; - else - TIMERG0.int_clr_timers.t0=1; - } -#endif - if(!(b->led->isOn())){ - b->led->on(); - timer_set_alarm_value(b->group,b->idx,b->onTime); - b->count--; - } else { - b->led->off(); - if(b->count){ - timer_set_alarm_value(b->group,b->idx,b->offTime); - } else { - timer_set_alarm_value(b->group,b->idx,b->delayTime); - b->count=b->nBlinks; + for(;;){ + for(int i=0;inBlinks;i++){ + b->led->on(); + delay(b->onTime); + b->led->off(); + delay(b->offTime); } + delay(b->delayTime); } - timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); - } ////////////////////////////////////// @@ -113,46 +64,48 @@ void Blinker::start(int period, float dutyCycle){ ////////////////////////////////////// void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ + + onTime=dutyCycle*period; + offTime=period-onTime; + this->delayTime=delayTime+offTime; + this->nBlinks=nBlinks; + + stop(); + Serial.printf("Starting Blink Task\n"); + xTaskCreate( blinkTask, "BlinkTask", 1024, (void *)this, 0, &blinkHandle ); pauseTime=millis(); isPaused=false; - - period*=10; - onTime=dutyCycle*period; - offTime=period-onTime; - this->delayTime=delayTime*10+offTime; - this->nBlinks=nBlinks; - count=nBlinks; - timer_set_counter_value(group,idx,0); - timer_set_alarm_value(group,idx,0); - timer_start(group,idx); } ////////////////////////////////////// void Blinker::stop(){ - timer_pause(group,idx); + if(blinkHandle!=NULL){ + Serial.printf("Deleting Blink Task\n"); + vTaskDelete(blinkHandle); + blinkHandle=NULL; + } + + isPaused=true; } ////////////////////////////////////// void Blinker::on(){ - pauseTime=millis(); - isPaused=false; - stop(); led->on(); + + pauseTime=millis(); + isPaused=false; } ////////////////////////////////////// void Blinker::off(){ - pauseTime=millis(); - isPaused=false; - stop(); led->off(); } @@ -164,9 +117,8 @@ void Blinker::check(){ if(pauseDuration==0 || isPaused || (millis()-pauseTime)off(); + Serial.print("Pausing LED\n"); + off(); } ////////////////////////////////////// diff --git a/src/extras/Blinker.h b/src/extras/Blinker.h index 9794d6b..16994ee 100644 --- a/src/extras/Blinker.h +++ b/src/extras/Blinker.h @@ -35,14 +35,11 @@ //////////////////////////////// class Blinkable { - protected: - boolean onState=false; - public: + virtual void on()=0; virtual void off()=0; virtual int getPin()=0; - boolean isOn() {return(onState);} }; //////////////////////////////// @@ -53,10 +50,10 @@ class LED : public Blinkable { int pin; public: - + LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} - void on() {digitalWrite(pin,HIGH);onState=true;} - void off() {digitalWrite(pin,LOW);onState=false;} + void on() {digitalWrite(pin,HIGH);} + void off() {digitalWrite(pin,LOW);} int getPin() {return(pin);} }; @@ -66,43 +63,28 @@ class LED : public Blinkable { class Blinker { - timer_group_t group; - timer_idx_t idx; - + TaskHandle_t blinkHandle = NULL; Blinkable *led; int nBlinks; int onTime; int offTime; int delayTime; - int count; unsigned long pauseDuration; unsigned long pauseTime; boolean isPaused=false; - static void isrTimer(void *arg); + static void blinkTask(void *arg); public: - Blinker(Blinkable *led, int timerNum=0, uint16_t autoOffDuration=0); + Blinker(Blinkable *led, uint16_t autoOffDuration=0); -// Creates a generic blinking LED on specified pin controlled -// in background via interrupts generated by an ESP32 Alarm Timer. -// -// In the first form, a Blinker is instantiated without specifying -// the pin. In this case the pin must be specified in a subsequent call -// to init() before the Blinker can be used. -// -// In the second form, a Blinker is instantiated and initialized with -// the specified pin, obviating the need for a separate call to init(). +// Creates a generic blinking LED in a separate task thread // // led: An initialized LED device that implements the Blinkable Interface -// -// timerNum: ESP32 Alarm Timer to use. -// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 -// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 -// +//// // autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed // Blinker will resume normal operation upon next call to start(), on(), or off() // Program must periodically call check() for auto-off functionality to work diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 27d431c..facd92d 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -39,7 +39,7 @@ // Single-Wire RGB/RGBW NeoPixels // //////////////////////////////////////////// -class Pixel : Blinkable { +class Pixel : public Blinkable { public: struct Color { @@ -152,6 +152,9 @@ class Pixel : Blinkable { operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); } + + void on() {set(RGB(255,0,0));} + void off() {set(RGB(0,0,0));} }; //////////////////////////////////////////// diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 8d47d19..3fd3fc5 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -1,13 +1,11 @@ // This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE, // as well as compile and test from this point. This file is ignored when the library is included in other sketches. +#include "Blinker.h" #include "Pixel.h" -//#define PixelType Pixel -#define PixelType Dot - -//Pixel p(8); -Dot p(0,1); +//Blinker p(new LED(26)); +Blinker p(new Pixel(27),20); void setup() { @@ -15,29 +13,21 @@ void setup() { Serial.flush(); delay(1000); // wait for interface to flush - Serial.println("\n\nHomeSpan Pixel Example\n"); + Serial.println("\n\nHomeSpan Blinker Example\n"); +// Serial.printf("Pins = %d %d\n",b.getPin(),p.getPin()); - PixelType::Color off=PixelType::RGB(0,0,0); - - p.set(PixelType::RGB(0,0,255),3); - delay(1000); - - p.set(off,3); - delay(1000); - - PixelType::Color c[]={p.HSV(120,100,30),p.HSV(0,0,0),p.HSV(0,0,0)}; - p.set(c,3); - delay(1000); - - c[0].HSV(0,0,0); - c[1].HSV(60,100,30); - p.set(c,3); - delay(1000); - - c[1].HSV(0,0,0); - c[2].HSV(0,100,30); - p.set(c,3); + p.on(); + delay(2000); + p.off(); + delay(2000); + p.start(300,0.25,4,1000); + delay(5000); + Serial.printf("New Pattern\n"); + p.start(200,0.2,2,200); + delay(3000); + p.off(); } void loop(){ + p.check(); } From 6fecf2c29f7985e6d530558256ceb3a10db3d033 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 28 Aug 2022 18:32:52 -0500 Subject: [PATCH 03/75] updating main code to incorporate new Blinkable class/interface --- src/HomeSpan.h | 6 +- src/Network.cpp | 2 - src/Utils.cpp | 149 ----------------------------------------- src/Utils.h | 118 -------------------------------- src/extras/Blinker.cpp | 2 +- src/extras/extras.ino | 2 +- src/src.ino | 1 - 7 files changed, 6 insertions(+), 274 deletions(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index dced380..534a3b5 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -41,6 +41,7 @@ #include #include "extras/Blinker.h" +#include "extras/Pixel.h" #include "Settings.h" #include "Utils.h" #include "Network.h" @@ -255,8 +256,9 @@ class Span{ boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false void setControlPin(uint8_t pin){controlButton=new PushButton(pin);} // sets Control Pin - void setStatusPin(uint8_t pin){statusLED=new Blinker(new LED(pin),0,autoOffLED);} // sets Status Pin -// void setStatusPin(Blinkable *led){statusLED=new Blinker(led,0,autoOffLED);} // sets Status Blinkable LED +// void setStatusPin(uint8_t pin){statusLED=new Blinker(new LED(pin),autoOffLED);} // sets Status Pin + void setStatusPin(uint8_t pin){statusLED=new Blinker(new Pixel(8),autoOffLED);} // sets Status Pin +// void setStatusPin(Blinkable *led){statusLED=new Blinker(led,autoOffLED);} // sets Status Blinkable LED void setStatusAutoOff(uint16_t duration){autoOffLED=duration;} // sets Status LED auto off (seconds) int getStatusPin(){return(statusLED?statusLED->getPin():-1);} // get Status Pin (returns -1 if undefined) diff --git a/src/Network.cpp b/src/Network.cpp index 47172cc..3118133 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -30,8 +30,6 @@ #include "Network.h" #include "HomeSpan.h" #include "Utils.h" -#include "extras/Blinker.h" - using namespace Utils; diff --git a/src/Utils.cpp b/src/Utils.cpp index 31b326d..21f57b4 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -35,7 +35,6 @@ // Utils::mask - masks a string with asterisks (good for displaying passwords) // // class PushButton - tracks Single, Double, and Long Presses of a pushbutton that connects a specified pin to ground -// class Blinker - creates customized blinking patterns on an LED connected to a specified pin // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -226,151 +225,3 @@ void PushButton::reset(){ #if SOC_TOUCH_SENSOR_NUM > 0 touch_value_t PushButton::threshold=0; #endif - -////////////////////////////////// -//// Blinker // -////////////////////////////////// -// -//Blinker::Blinker(Blinkable *led, int timerNum, uint16_t autoOffDuration){ -// -// this->led=led; -// -// pauseDuration=autoOffDuration*1000; -// -//#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group -// group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; -// idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group -//#else -// group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; -// idx=TIMER_0; -//#endif -// -// timer_config_t conf; -// conf.alarm_en=TIMER_ALARM_EN; -// conf.counter_en=TIMER_PAUSE; -// conf.intr_type=TIMER_INTR_LEVEL; -// conf.counter_dir=TIMER_COUNT_UP; -// conf.auto_reload=TIMER_AUTORELOAD_EN; -// conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) -// -//#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure -// conf.clk_src=TIMER_SRC_CLK_APB; -//#endif -// -// timer_init(group,idx,&conf); -// timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); -// timer_enable_intr(group,idx); -// -//} -// -//////////////////////////////////////// -// -//void Blinker::isrTimer(void *arg){ -// -// Blinker *b=(Blinker *)arg; -// -//#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 -// timer_group_clr_intr_status_in_isr(b->group,b->idx); -//#else // use older method that is only for ESP32 -// if(b->group){ -// if(b->idx) -// TIMERG1.int_clr_timers.t1=1; -// else -// TIMERG1.int_clr_timers.t0=1; -// } else { -// if(b->idx) -// TIMERG0.int_clr_timers.t1=1; -// else -// TIMERG0.int_clr_timers.t0=1; -// } -//#endif -// -// if(!(b->led->isOn())){ -// b->led->on(); -// timer_set_alarm_value(b->group,b->idx,b->onTime); -// b->count--; -// } else { -// b->led->off(); -// if(b->count){ -// timer_set_alarm_value(b->group,b->idx,b->offTime); -// } else { -// timer_set_alarm_value(b->group,b->idx,b->delayTime); -// b->count=b->nBlinks; -// } -// } -// -// timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); -// -//} -// -//////////////////////////////////////// -// -//void Blinker::start(int period, float dutyCycle){ -// -// start(period, dutyCycle, 1, 0); -//} -// -//////////////////////////////////////// -// -//void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ -// -// pauseTime=millis(); -// isPaused=false; -// -// period*=10; -// onTime=dutyCycle*period; -// offTime=period-onTime; -// this->delayTime=delayTime*10+offTime; -// this->nBlinks=nBlinks; -// count=nBlinks; -// timer_set_counter_value(group,idx,0); -// timer_set_alarm_value(group,idx,0); -// timer_start(group,idx); -//} -// -//////////////////////////////////////// -// -//void Blinker::stop(){ -// -// timer_pause(group,idx); -//} -// -//////////////////////////////////////// -// -//void Blinker::on(){ -// -// pauseTime=millis(); -// isPaused=false; -// -// stop(); -// led->on(); -//} -// -//////////////////////////////////////// -// -//void Blinker::off(){ -// -// pauseTime=millis(); -// isPaused=false; -// -// stop(); -// led->off(); -//} -// -//////////////////////////////////////// -// -//void Blinker::check(){ -// -// if(pauseDuration==0 || isPaused || (millis()-pauseTime)off(); -//} -// -//////////////////////////////////////// -// -//int Blinker::getPin(){ -// return(led->getPin()); -//} diff --git a/src/Utils.h b/src/Utils.h index 7af49ed..2883465 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -29,7 +29,6 @@ #include #include -#include "extras/Blinker.h" namespace Utils { @@ -38,21 +37,6 @@ String mask(char *c, int n); // simply utility that creates a String fr } -//////////////////////////////// -// Blinkable Interface // -//////////////////////////////// - -//class Blinkable { -// protected: -// boolean onState=false; -// -// public: -// virtual void on()=0; -// virtual void off()=0; -// virtual int getPin()=0; -// boolean isOn() {return(onState);} -//}; - ///////////////////////////////////////////////// // Creates a temporary buffer that is freed after // going out of scope @@ -197,105 +181,3 @@ class PushButton{ #endif }; - - - -////////////////////////////////// -//// Generic_LED // -////////////////////////////////// -// -//class LED : public Blinkable { -// int pin; -// -// public: -// -// LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} -// void on() {digitalWrite(pin,HIGH);onState=true;} -// void off() {digitalWrite(pin,LOW);onState=false;} -// int getPin() {return(pin);} -//}; - -//////////////////////////////// -// Blinker // -//////////////////////////////// - -//class Blinker { -// -// timer_group_t group; -// timer_idx_t idx; -// -// Blinkable *led; -// -// int nBlinks; -// int onTime; -// int offTime; -// int delayTime; -// int count; -// -// unsigned long pauseDuration; -// unsigned long pauseTime; -// boolean isPaused=false; -// -// static void isrTimer(void *arg); -// -// public: -// -// Blinker(Blinkable *led, int timerNum=0, uint16_t autoOffDuration=0); -// -//// Creates a generic blinking LED on specified pin controlled -//// in background via interrupts generated by an ESP32 Alarm Timer. -//// -//// In the first form, a Blinker is instantiated without specifying -//// the pin. In this case the pin must be specified in a subsequent call -//// to init() before the Blinker can be used. -//// -//// In the second form, a Blinker is instantiated and initialized with -//// the specified pin, obviating the need for a separate call to init(). -//// -//// led: An initialized LED device that implements the Blinkable Interface -//// -//// timerNum: ESP32 Alarm Timer to use. -//// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 -//// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 -//// -//// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed -//// Blinker will resume normal operation upon next call to start(), on(), or off() -//// Program must periodically call check() for auto-off functionality to work -// -// void start(int period, float dutyCycle=0.5); -// -//// Starts simple ON/OFF blinking. -//// -//// period: ON/OFF blinking period, in milliseconds -//// dutyCycle: Fraction of period that LED is ON (default=50%) -// -// void start(int period, float dutyCycle, int nBlinks, int delayTime); -// -//// Starts ON/OFF blinking pattern. -//// -//// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern -//// dutyCycle: Fraction of period that LED is ON (default=50%) -//// nBlinks: Number of blinks in blinking portion of pattern -//// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern -// -// void stop(); -// -//// Stops current blinking pattern. -// -// void on(); -// -//// Stops current blinking pattern and turns on LED -// -// void off(); -// -//// Stops current blinking pattern and turns off LED -// -// void check(); -// -//// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) -// -// int getPin(); -// -//// Returns pin number of connected LED -// -//}; diff --git a/src/extras/Blinker.cpp b/src/extras/Blinker.cpp index 9e65d48..8f1dd62 100644 --- a/src/extras/Blinker.cpp +++ b/src/extras/Blinker.cpp @@ -72,7 +72,7 @@ void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ stop(); Serial.printf("Starting Blink Task\n"); - xTaskCreate( blinkTask, "BlinkTask", 1024, (void *)this, 0, &blinkHandle ); + xTaskCreate( blinkTask, "BlinkTask", 1024, (void *)this, 2, &blinkHandle ); pauseTime=millis(); isPaused=false; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 3fd3fc5..f44cf48 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -5,7 +5,7 @@ #include "Pixel.h" //Blinker p(new LED(26)); -Blinker p(new Pixel(27),20); +Blinker p(new Pixel(8)); void setup() { diff --git a/src/src.ino b/src/src.ino index 6e0bc53..a0ba37e 100644 --- a/src/src.ino +++ b/src/src.ino @@ -5,7 +5,6 @@ #include "HomeSpan.h" #include "FeatherPins.h" #include "extras/Pixel.h" -#include "extras/Blinker.h" #define STRING_t const char * // WORK-AROUND From f2e1f5bc70b28ad92f76a9e08c13a382e9ce10d5 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 2 Sep 2022 18:15:53 -0500 Subject: [PATCH 04/75] Updated Blinker Class to allow for NULL Blinkable Device If NULL, all functions are ignored and getPin() returns -1 --- src/HomeSpan.cpp | 2 ++ src/HomeSpan.h | 11 +++++------ src/extras/Blinker.cpp | 21 ++++++++++++++++++++- src/extras/extras.ino | 9 ++++----- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index b59b237..88481b4 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -57,6 +57,8 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa this->modelName=modelName; sprintf(this->category,"%d",(int)catID); + statusLED=new Blinker(statusDevice,autoOffLED); + esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); // required to avoid watchdog timeout messages from ESP32-C3 if(requestedMaxCongetPin():-1);} // get Status Pin (returns -1 if undefined) int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined) diff --git a/src/extras/Blinker.cpp b/src/extras/Blinker.cpp index 8f1dd62..93130a0 100644 --- a/src/extras/Blinker.cpp +++ b/src/extras/Blinker.cpp @@ -64,7 +64,10 @@ void Blinker::start(int period, float dutyCycle){ ////////////////////////////////////// void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ - + + if(!led) + return; + onTime=dutyCycle*period; offTime=period-onTime; this->delayTime=delayTime+offTime; @@ -81,6 +84,9 @@ void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ ////////////////////////////////////// void Blinker::stop(){ + + if(!led) + return; if(blinkHandle!=NULL){ Serial.printf("Deleting Blink Task\n"); @@ -95,6 +101,9 @@ void Blinker::stop(){ void Blinker::on(){ + if(!led) + return; + stop(); led->on(); @@ -106,6 +115,9 @@ void Blinker::on(){ void Blinker::off(){ + if(!led) + return; + stop(); led->off(); } @@ -114,6 +126,9 @@ void Blinker::off(){ void Blinker::check(){ + if(!led) + return; + if(pauseDuration==0 || isPaused || (millis()-pauseTime)getPin()); } diff --git a/src/extras/extras.ino b/src/extras/extras.ino index f44cf48..b179ae1 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -3,9 +3,10 @@ #include "Blinker.h" #include "Pixel.h" +#include -//Blinker p(new LED(26)); -Blinker p(new Pixel(8)); +Blinker p(new Pixel(2),10); +//Blinker p(NULL,10); void setup() { @@ -14,7 +15,7 @@ void setup() { delay(1000); // wait for interface to flush Serial.println("\n\nHomeSpan Blinker Example\n"); -// Serial.printf("Pins = %d %d\n",b.getPin(),p.getPin()); + Serial.printf("Pins = %d\n",p.getPin()); p.on(); delay(2000); @@ -24,8 +25,6 @@ void setup() { delay(5000); Serial.printf("New Pattern\n"); p.start(200,0.2,2,200); - delay(3000); - p.off(); } void loop(){ From 355a2dfd4d54143c32c63eb6334a6cd4b165240a Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 2 Sep 2022 18:47:15 -0500 Subject: [PATCH 05/75] Updated rest of code to reflect latest version of statusLED Removed all checks to see if statusLED is defined, since it will now ALWAYS be defined, even if the statusDevice is set to NULL. --- src/HAP.cpp | 6 ++-- src/HomeSpan.cpp | 67 ++++++++++++++++-------------------------- src/HomeSpan.h | 2 +- src/Network.cpp | 24 +++++---------- src/extras/Blinker.cpp | 2 -- 5 files changed, 36 insertions(+), 65 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index a44b9ee..4b163af 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -638,8 +638,7 @@ int HAPClient::postPairSetupURL(){ LOG1("\n*** ACCESSORY PAIRED! ***\n"); - if(homeSpan.statusLED) - homeSpan.statusLED->on(); + homeSpan.statusLED->on(); if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(true); @@ -1638,8 +1637,7 @@ void HAPClient::removeController(uint8_t *id){ removeControllers(); LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_PAIRING_NEEDED); + homeSpan.statusLED->start(LED_PAIRING_NEEDED); if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(false); } diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 88481b4..be8eb37 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -57,7 +57,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa this->modelName=modelName; sprintf(this->category,"%d",(int)catID); - statusLED=new Blinker(statusDevice,autoOffLED); + statusLED=new Blinker(statusDevice,autoOffLED); // create Status LED, even is statusDevice is NULL esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); // required to avoid watchdog timeout messages from ESP32-C3 @@ -189,10 +189,9 @@ void Span::pollTask() { processSerialCommand("A"); } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); - if(statusLED) - statusLED->start(LED_WIFI_NEEDED); + statusLED->start(LED_WIFI_NEEDED); } - } else if(statusLED) { + } else { statusLED->start(LED_WIFI_CONNECTING); } @@ -291,12 +290,11 @@ void Span::pollTask() { if(spanOTA.enabled) ArduinoOTA.handle(); - if(controlButton && controlButton->primed() && statusLED) + if(controlButton && controlButton->primed()) statusLED->start(LED_ALERT); if(controlButton && controlButton->triggered(3000,10000)){ - if(statusLED) - statusLED->off(); + statusLED->off(); if(controlButton->type()==PushButton::LONG){ controlButton->wait(); processSerialCommand("F"); // FACTORY RESET @@ -305,8 +303,7 @@ void Span::pollTask() { } } - if(statusLED) - statusLED->check(); + statusLED->check(); } // poll @@ -326,7 +323,7 @@ int Span::getFreeSlot(){ void Span::commandMode(){ - if(!statusLED){ + if(!statusDevice){ Serial.print("*** ERROR: CAN'T ENTER COMMAND MODE WITHOUT A DEFINED STATUS LED***\n\n"); return; } @@ -409,8 +406,7 @@ void Span::checkConnect(){ connected++; waitTime=60000; alarmConnect=0; - if(statusLED) - statusLED->start(LED_WIFI_CONNECTING); + statusLED->start(LED_WIFI_CONNECTING); } if(WiFi.status()!=WL_CONNECTED){ @@ -437,12 +433,10 @@ void Span::checkConnect(){ return; } - if(statusLED){ - if(!HAPClient::nAdminControllers()) - statusLED->start(LED_PAIRING_NEEDED); - else - statusLED->on(); - } + if(!HAPClient::nAdminControllers()) + statusLED->start(LED_PAIRING_NEEDED); + else + statusLED->on(); connected++; @@ -767,12 +761,10 @@ void Span::processSerialCommand(const char *c){ Serial.print("\nDEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - if(statusLED){ - if(strlen(network.wifiData.ssid)==0) - statusLED->start(LED_WIFI_NEEDED); - else - statusLED->start(LED_PAIRING_NEEDED); - } + if(strlen(network.wifiData.ssid)==0) + statusLED->start(LED_WIFI_NEEDED); + else + statusLED->start(LED_PAIRING_NEEDED); } break; @@ -789,8 +781,7 @@ void Span::processSerialCommand(const char *c){ nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data nvs_commit(wifiNVS); // commit to NVS Serial.print("\n*** WiFi Credentials SAVED! Re-starting ***\n\n"); - if(statusLED) - statusLED->off(); + statusLED->off(); delay(1000); ESP.restart(); } @@ -823,8 +814,7 @@ void Span::processSerialCommand(const char *c){ } Serial.print("\n*** Re-starting ***\n\n"); - if(statusLED) - statusLED->off(); + statusLED->off(); delay(1000); ESP.restart(); // re-start device } @@ -832,8 +822,7 @@ void Span::processSerialCommand(const char *c){ case 'X': { - if(statusLED) - statusLED->off(); + statusLED->off(); nvs_erase_all(wifiNVS); nvs_commit(wifiNVS); Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); @@ -852,8 +841,7 @@ void Span::processSerialCommand(const char *c){ case 'H': { - if(statusLED) - statusLED->off(); + statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); Serial.print("\n*** HomeSpan Device ID and Pairing Data DELETED! Restarting...\n\n"); @@ -864,8 +852,7 @@ void Span::processSerialCommand(const char *c){ case 'R': { - if(statusLED) - statusLED->off(); + statusLED->off(); Serial.print("\n*** Restarting...\n\n"); delay(1000); ESP.restart(); @@ -874,8 +861,7 @@ void Span::processSerialCommand(const char *c){ case 'F': { - if(statusLED) - statusLED->off(); + statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); nvs_erase_all(wifiNVS); @@ -892,8 +878,7 @@ void Span::processSerialCommand(const char *c){ case 'E': { - if(statusLED) - statusLED->off(); + statusLED->off(); nvs_flash_erase(); Serial.print("\n*** ALL DATA ERASED! Restarting...\n\n"); delay(1000); @@ -2117,8 +2102,7 @@ void SpanOTA::start(){ Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); otaPercent=0; - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_OTA_STARTED); + homeSpan.statusLED->start(LED_OTA_STARTED); } /////////////////////////////// @@ -2127,8 +2111,7 @@ void SpanOTA::end(){ nvs_set_u8(homeSpan.otaNVS,"OTA_REQUIRED",safeLoad); nvs_commit(homeSpan.otaNVS); Serial.printf(" DONE! Rebooting...\n"); - if(homeSpan.statusLED) - homeSpan.statusLED->off(); + homeSpan.statusLED->off(); delay(100); // make sure commit it finished before reboot } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index c973a30..d378a47 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -260,7 +260,7 @@ class Span{ void setStatusPin(uint8_t pin){statusDevice=new LED(pin);} // sets Status Device to a simple LED on specified pin void setStatusDevice(Blinkable *dev){statusDevice=dev;} // sets Status Device to generic Blinkable object void setStatusAutoOff(uint16_t duration){autoOffLED=duration;} // sets Status LED auto off (seconds) - int getStatusPin(){return(statusLED?statusLED->getPin():-1);} // get Status Pin (returns -1 if undefined) + int getStatusPin(){return(statusLED->getPin());} // get Status Pin (getPin will return -1 if underlying statusDevice is undefined) int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined) void setApSSID(const char *ssid){network.apSSID=ssid;} // sets Access Point SSID void setApPassword(const char *pwd){network.apPassword=pwd;} // sets Access Point Password diff --git a/src/Network.cpp b/src/Network.cpp index 3118133..9a3dbc2 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -116,8 +116,7 @@ void Network::apConfigure(){ Serial.print(apPassword); Serial.print("\n"); - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_AP_STARTED); + homeSpan.statusLED->start(LED_AP_STARTED); Serial.print("\nScanning for Networks...\n\n"); @@ -155,12 +154,10 @@ void Network::apConfigure(){ if(homeSpan.controlButton && homeSpan.controlButton->triggered(9999,3000)){ Serial.print("\n*** Access Point Terminated."); - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_ALERT); + homeSpan.statusLED->start(LED_ALERT); homeSpan.controlButton->wait(); Serial.print(" Restarting... \n\n"); - if(homeSpan.statusLED) - homeSpan.statusLED->off(); + homeSpan.statusLED->off(); ESP.restart(); } @@ -179,11 +176,9 @@ void Network::apConfigure(){ Serial.print("\n*** Access Point: Configuration Canceled."); } Serial.print(" Restarting...\n\n"); - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_ALERT); + homeSpan.statusLED->start(LED_ALERT); delay(1000); - if(homeSpan.statusLED) - homeSpan.statusLED->off(); + homeSpan.statusLED->off(); ESP.restart(); } } @@ -278,8 +273,7 @@ void Network::processRequest(char *body, char *formData){ getFormValue(formData,"network",wifiData.ssid,MAX_SSID); getFormValue(formData,"pwd",wifiData.pwd,MAX_PWD); - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_WIFI_CONNECTING); + homeSpan.statusLED->start(LED_WIFI_CONNECTING); responseBody+="" "

Initiating WiFi connection to:

" + String(wifiData.ssid) + "

"; @@ -326,8 +320,7 @@ void Network::processRequest(char *body, char *formData){ } else { - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_AP_CONNECTED); // slow double-blink + homeSpan.statusLED->start(LED_AP_CONNECTED); // slow double-blink responseBody+="

SUCCESS! Connected to:

" + String(wifiData.ssid) + "

"; responseBody+="

You may enter new 8-digit Setup Code below, or leave blank to retain existing code.

"; @@ -347,8 +340,7 @@ void Network::processRequest(char *body, char *formData){ LOG1("In Landing Page...\n"); - if(homeSpan.statusLED) - homeSpan.statusLED->start(LED_AP_CONNECTED); + homeSpan.statusLED->start(LED_AP_CONNECTED); waitTime=2; responseBody+="

Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.

" diff --git a/src/extras/Blinker.cpp b/src/extras/Blinker.cpp index 93130a0..e97ee34 100644 --- a/src/extras/Blinker.cpp +++ b/src/extras/Blinker.cpp @@ -74,7 +74,6 @@ void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ this->nBlinks=nBlinks; stop(); - Serial.printf("Starting Blink Task\n"); xTaskCreate( blinkTask, "BlinkTask", 1024, (void *)this, 2, &blinkHandle ); pauseTime=millis(); @@ -89,7 +88,6 @@ void Blinker::stop(){ return; if(blinkHandle!=NULL){ - Serial.printf("Deleting Blink Task\n"); vTaskDelete(blinkHandle); blinkHandle=NULL; } From 546c87e3ecd136cc49ef7f51f7272e7adbf57169 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 3 Sep 2022 16:47:02 -0500 Subject: [PATCH 06/75] Added setOnColor to Pixel Allows user to set the color to be used for Blinker --- src/extras/Blinker.h | 15 --------------- src/extras/Pixel.cpp | 4 +++- src/extras/Pixel.h | 8 +++++--- src/extras/PwmPin.h | 20 +++++++++++++++++++- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/extras/Blinker.h b/src/extras/Blinker.h index 16994ee..13aec3f 100644 --- a/src/extras/Blinker.h +++ b/src/extras/Blinker.h @@ -42,21 +42,6 @@ class Blinkable { virtual int getPin()=0; }; -//////////////////////////////// -// Generic_LED // -//////////////////////////////// - -class LED : public Blinkable { - int pin; - - public: - - LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} - void on() {digitalWrite(pin,HIGH);} - void off() {digitalWrite(pin,LOW);} - int getPin() {return(pin);} -}; - //////////////////////////////// // Blinker // //////////////////////////////// diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 479d115..1e87187 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -58,7 +58,9 @@ Pixel::Pixel(int pin, boolean isRGBW){ rmt_set_tx_intr_en(rf->getChannel(),false); // disable end-of-transmission interrupt txEndMask=RMT.int_ena.val; // save interrupt enable vector rmt_set_tx_intr_en(rf->getChannel(),true); // enable end-of-transmission interrupt - txEndMask^=RMT.int_ena.val; // find bit that flipped and save as end-of-transmission mask for this channel + txEndMask^=RMT.int_ena.val; // find bit that flipped and save as end-of-transmission mask for this channel + + onColor.HSV(0,100,100,0); } /////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index facd92d..e781129 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -132,13 +132,14 @@ class Pixel : public Blinkable { uint32_t txEndMask; // mask for end-of-transmission interrupt uint32_t txThrMask; // mask for threshold interrupt uint32_t lastBit; // 0=RGBW; 8=RGB + Color onColor; // color used for on() command const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel static void loadData(void *arg); // interrupt handler volatile static pixel_status_t status; // storage for volatile information modified in interupt handler - public: + public: Pixel(int pin, boolean isRGBW=false); // creates addressable single-wire RGB (false) or RGBW (true) LED connected to pin (such as the SK68 or WS28) void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c @@ -153,8 +154,9 @@ class Pixel : public Blinkable { return(*rf); } - void on() {set(RGB(255,0,0));} - void off() {set(RGB(0,0,0));} + void on() {set(onColor);} + void off() {set(RGB(0,0,0,0));} + Pixel *setOnColor(Color c){onColor=c;return(this);} }; //////////////////////////////////////////// diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index c3d3f4c..3edecc3 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -44,6 +44,7 @@ #include #include +#include "Blinker.h" #define DEFAULT_PWM_FREQ 5000 @@ -80,7 +81,24 @@ class LedPin : public LedC { static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B }; -///////////////////////////////////// +//////////////////////////////// +// LED // +//////////////////////////////// + +class LED : public Blinkable { + int pin; + + public: + + LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} + void on() {digitalWrite(pin,HIGH);} + void off() {digitalWrite(pin,LOW);} + int getPin() {return(pin);} +}; + +//////////////////////////////// +// ServoPin // +//////////////////////////////// class ServoPin : public LedC { uint16_t minMicros; From 8b9487b9d44829c51029a7f72ba77b8ba58674fe Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 4 Sep 2022 07:31:08 -0500 Subject: [PATCH 07/75] Created homeSpan.setStatusPixel(float h=0, float s=100, float v=100) This is now the official API method to set an RGB NeoPixel as the status LED. It defaults to bright red unless an HSV color is specified. --- src/HomeSpan.h | 8 ++++++-- src/extras/Blinker.h | 15 +++++++++++++++ src/extras/PwmPin.h | 13 ------------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index d378a47..f24a893 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -257,11 +257,15 @@ class Span{ boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false void setControlPin(uint8_t pin){controlButton=new PushButton(pin);} // sets Control Pin - void setStatusPin(uint8_t pin){statusDevice=new LED(pin);} // sets Status Device to a simple LED on specified pin - void setStatusDevice(Blinkable *dev){statusDevice=dev;} // sets Status Device to generic Blinkable object + void setStatusPin(uint8_t pin){statusDevice=new GenericLED(pin);} // sets Status Device to a simple LED on specified pin void setStatusAutoOff(uint16_t duration){autoOffLED=duration;} // sets Status LED auto off (seconds) int getStatusPin(){return(statusLED->getPin());} // get Status Pin (getPin will return -1 if underlying statusDevice is undefined) int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined) + + void setStatusPixel(uint8_t pin,float h=0,float s=100,float v=100){ // sets Status Device to an RGB Pixel on specified pin + statusDevice=((new Pixel(pin))->setOnColor(Pixel::HSV(h,s,v))); + } + void setApSSID(const char *ssid){network.apSSID=ssid;} // sets Access Point SSID void setApPassword(const char *pwd){network.apPassword=pwd;} // sets Access Point Password void setApTimeout(uint16_t nSec){network.lifetime=nSec*1000;} // sets Access Point Timeout (seconds) diff --git a/src/extras/Blinker.h b/src/extras/Blinker.h index 13aec3f..633bc19 100644 --- a/src/extras/Blinker.h +++ b/src/extras/Blinker.h @@ -111,3 +111,18 @@ class Blinker { // Returns pin number of connected LED }; + +//////////////////////////////// +// GenericLED // +//////////////////////////////// + +class GenericLED : public Blinkable { + int pin; + + public: + + GenericLED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} + void on() {digitalWrite(pin,HIGH);} + void off() {digitalWrite(pin,LOW);} + int getPin() {return(pin);} +}; diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 3edecc3..d9506ec 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -81,20 +81,7 @@ class LedPin : public LedC { static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B }; -//////////////////////////////// -// LED // -//////////////////////////////// -class LED : public Blinkable { - int pin; - - public: - - LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} - void on() {digitalWrite(pin,HIGH);} - void off() {digitalWrite(pin,LOW);} - int getPin() {return(pin);} -}; //////////////////////////////// // ServoPin // From 677b9b673c90b6ec4fb0e04f280ffa17d8172554 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 4 Sep 2022 10:03:59 -0500 Subject: [PATCH 08/75] Update FeatherPins.h Added BUILTIN_PIXEL for S2, C3, and S3 chips --- src/FeatherPins.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/FeatherPins.h b/src/FeatherPins.h index 7072d86..b41aa7e 100644 --- a/src/FeatherPins.h +++ b/src/FeatherPins.h @@ -29,7 +29,7 @@ // Facilitates the testing of identical code on an ESP32, ESP32-S2, and ESP32-C3 using a common jig without rewiring #pragma once - + #if defined(ARDUINO_FEATHER_ESP32) enum { F13=13,F12=12,F27=27,F15=15,F32=32,F14=14,F16=16,F17=17,F21=21, // Digital Only (9 pins) @@ -44,7 +44,8 @@ F13=1,F12=3,F27=7,F15=10,F32=42,F14=11,F16=20,F17=21,F21=16, // Digital Only (9 pins) F26=17,F25=14,F34=13,F39=12,F36=18,F4=19, // A0-A5 F22=9,F23=8, // I2C SCL/SDA - F5=36,F18=35,F19=37,F33=34 // SPI SCK/SDO/SDI/CS + F5=36,F18=35,F19=37,F33=34, // SPI SCK/SDO/SDI/CS + BUILTIN_PIXEL=18 // Built-in Neo-Pixel }; #define DEVICE_SUFFIX "-S2" @@ -53,7 +54,8 @@ F27=2,F32=3,F14=10,F16=20,F17=21,F21=19, // Digital Only (6 pins) F26=0,F25=1,F4=18, // A0/A1/A5 F22=9,F23=8, // I2C SCL/SDA - F5=4,F18=6,F19=5,F33=7 // SPI SCK/SDO/SDI/CS + F5=4,F18=6,F19=5,F33=7, // SPI SCK/SDO/SDI/CS + BUILTIN_PIXEL=8 // Built-in Neo-Pixel }; #define DEVICE_SUFFIX "-C3" @@ -62,7 +64,8 @@ F13=5,F12=6,F27=7,F15=16,F32=17,F14=18,F16=37,F17=36,F21=35, // Digital Only (9 pins) F26=1,F25=2,F34=20,F39=19,F36=15,F4=4, // A0-A5 F22=9,F23=8, // I2C SCL/SDA - F5=12,F18=11,F19=13,F33=10 // SPI SCK/SDO/SDI/CS + F5=12,F18=11,F19=13,F33=10, // SPI SCK/SDO/SDI/CS + BUILTIN_PIXEL=48 // Built-in Neo-Pixel }; #define DEVICE_SUFFIX "-S3" From 8975ec992222dc567be0669e66f09aa2a05d3c99 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 4 Sep 2022 14:26:31 -0500 Subject: [PATCH 09/75] Update Reference.md --- docs/Reference.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index 8f7348c..024f2ca 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -30,9 +30,26 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * `void setControlPin(uint8_t pin)` * sets the ESP32 pin to use for the HomeSpan Control Button. If not specified, HomeSpan will assume there is no Control Button + +* `int getControlPin()` + * returns the pin number of the HomeSpan Control Button as set by `setControlPin(pin)`, or -1 if no pin has been set * `void setStatusPin(uint8_t pin)` - * sets the ESP32 pin to use for the HomeSpan Status LED. If not specified, HomeSpan will assume there is no Status LED + * sets the ESP32 pin to use for the HomeSpan Status LED + * assumes a standard LED will be connected to *pin* + * if neither this method nor `setStatusPixel()` is called, HomeSpan will assume there is no Status LED + +* `void setStatusPixel(uint8_t pin, float h=0, float s=100, float v=100)` + * sets the ESP32 pin to use for the HomeSpan Status LED + * assumes an RGB NeoPixel (or equivalent) will be connected to *pin* + * works well with ESP32 boards that have a built-in NeoPixel LED, though adding an external NeoPixel is fine + * users can optionally specify the color HomeSpan will use with the NeoPixel by providing the following HSV values: + * h = Hue from 0-360 + * s = Saturation percentage from 0-100 + * v = Brightness percentage from 0-100 + * color defaults to *red* if unspecified + * example: `homeSpan.setStatusPixel(8,120,100,20)` sets the Status LED to light green using a NeoPixel attached to pin 8 + * if neither this method nor `setStatusPin()` is called, HomeSpan will assume there is no Status LED * `void setStatusAutoOff(uint16_t duration)` * sets Status LED to automatically turn off after *duration* seconds From 4a4a5f8a6b063db1197166cc886dcc531c877360 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 5 Sep 2022 18:42:37 -0500 Subject: [PATCH 10/75] Started development of ESP-NOW Also, added WiFi.begin("none") to make sure all WiFi data is erased when requested. --- src/HomeSpan.cpp | 20 +++++++++++++++----- src/HomeSpan.h | 2 ++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index be8eb37..947ffaa 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "HomeSpan.h" #include "HAP.h" @@ -137,7 +138,13 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa Serial.print(__TIME__); Serial.printf("\nPartition: %s",esp_ota_get_running_partition()->label); - + + if(espNowEnabled){ + WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. AP mode will not be started, but WiFi will be properly configured for use with ESP-NOW + esp_now_init(); // initialize ESP NOW + Serial.print("\nESP-NOW: ENABLED"); + } + Serial.print("\n\nDevice Name: "); Serial.print(displayName); Serial.print("\n\n"); @@ -151,7 +158,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa delay(100); esp_ota_mark_app_invalid_rollback_and_reboot(); } - + } // begin /////////////////////////////// @@ -440,7 +447,8 @@ void Span::checkConnect(){ connected++; - addWebLog(true,"WiFi Connected! IP Address = %s\n",WiFi.localIP().toString().c_str()); + addWebLog(true,"WiFi Connected! IP Address = %s",WiFi.localIP().toString().c_str()); + Serial.printf("MAC Address = %s, Channel = %d\n",WiFi.macAddress().c_str(),WiFi.channel()); if(connected>1) // Do not initialize everything below if this is only a reconnect return; @@ -824,7 +832,8 @@ void Span::processSerialCommand(const char *c){ statusLED->off(); nvs_erase_all(wifiNVS); - nvs_commit(wifiNVS); + nvs_commit(wifiNVS); + WiFi.begin("none"); Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); delay(1000); ESP.restart(); // re-start device @@ -869,7 +878,8 @@ void Span::processSerialCommand(const char *c){ nvs_erase_all(charNVS); nvs_commit(charNVS); nvs_erase_all(otaNVS); - nvs_commit(otaNVS); + nvs_commit(otaNVS); + WiFi.begin("none"); Serial.print("\n*** FACTORY RESET! Restarting...\n\n"); delay(1000); ESP.restart(); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index f24a893..205aac8 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -185,6 +185,7 @@ class Span{ char pairingCodeCommand[12]=""; // user-specified Pairing Code - only needed if Pairing Setup Code is specified in sketch using setPairingCode() String lastClientIP="0.0.0.0"; // IP address of last client accessing device through encrypted channel boolean newCode; // flag indicating new application code has been loaded (based on keeping track of app SHA256) + boolean espNowEnabled=false; // flag indicating if ESP-NOW is enabled int connected=0; // WiFi connection status (increments upon each connect and disconnect) unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts @@ -283,6 +284,7 @@ class Span{ void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials + void enableEspNOW(){espNowEnabled=true;}; // enables ESP-NOW void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS From 66160e7c6c827b5ca63a350910fe181c07be3847 Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 22 Sep 2022 20:26:34 -0500 Subject: [PATCH 11/75] continued development of espnow --- src/HomeSpan.h | 1 + src/Now.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Now.h | 25 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 src/Now.cpp create mode 100644 src/Now.h diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 205aac8..02ac360 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -48,6 +48,7 @@ #include "HAPConstants.h" #include "HapQR.h" #include "Characteristics.h" +#include "Now.h" using std::vector; using std::unordered_map; diff --git a/src/Now.cpp b/src/Now.cpp new file mode 100644 index 0000000..18ed780 --- /dev/null +++ b/src/Now.cpp @@ -0,0 +1,66 @@ + +#include "Now.h" +#include +#include +#include + +void SpanNow::start(const char *macAddress, const char *password){ + + if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",mac,mac+1,mac+2,mac+3,mac+4,mac+5)!=6){ + Serial.printf("*** ERROR: Can't start HomeSpan NOW! Bad MAC Address '%s'\n\n",macAddress); + return; + } + + statusQueue = xQueueCreate(1,sizeof(esp_now_send_status_t)); + + WiFi.mode(WIFI_AP_STA); + esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + esp_now_init(); + esp_now_register_send_cb(onDataSent); + + uint8_t lmk[32]; + + mbedtls_sha256_ret((const unsigned char *)password,strlen(password),lmk,0); + esp_now_set_pmk(lmk+16); + + memcpy(peerInfo.peer_addr, mac, 6); + peerInfo.encrypt = true; + memcpy(peerInfo.lmk, lmk, 16); + esp_now_add_peer(&peerInfo); + esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + + Serial.printf("Started HomeSpan NOW on Channel=%d. Hub MAC Address: %X:%X:%X:%X:%X:%X\n",channel,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); + started=true; +} + +boolean SpanNow::send(uint8_t *data, size_t len){ + + if(!started){ + Serial.printf("*** ERROR: Can't send data until HomeSpanNOW has been started.\n\n"); + return(false); + } + + esp_now_send_status_t status = ESP_NOW_SEND_FAIL; + + for(int c=0;c<13;c++){ + if((1< +#include + + +class SpanNow { + + esp_now_peer_info_t peerInfo; + uint8_t mac[6]; + boolean started=false; + static QueueHandle_t statusQueue; + int channel=1; + uint16_t channelMask=0x3FFE; + + static void onDataSent(const uint8_t *mac, esp_now_send_status_t status) { + xQueueOverwrite( statusQueue, &status ); + } + + public: + + void setChannelMask(uint16_t cm){channelMask = cm & 0x3FFE;} + void start(const char *macAddress, const char *password="HomeSpan"); + boolean send(uint8_t *data, size_t len); + +}; From dbe4c268f4b8aa2c7d5d4687fd44b4762ef7d13d Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 24 Sep 2022 08:20:22 -0500 Subject: [PATCH 12/75] Modified folder structure for "extras" This allows the "src.ino" test file to once again properly compile without requiring modifications to any existing HomeSpan sketches. This was needed to ensure src.ino can find object code in "extras", which is now required to use the new statusDevice() method. Since the Arduino IDE only copies files that are in "src" folders, "extras" needed to be moved to "src/extras". For backwards compatibility with all other sketches, the "extras" directory now contains stubs for the ".h" include files. --- src/extras/Blinker.h | 100 +---------- src/extras/Pixel.h | 232 +------------------------- src/extras/PwmPin.h | 74 +-------- src/extras/RFControl.h | 51 +----- src/src.ino | 4 + src/{ => src}/extras/Blinker.cpp | 0 src/src/extras/Blinker.h | 128 ++++++++++++++ src/{ => src}/extras/Pixel.cpp | 0 src/src/extras/Pixel.h | 259 +++++++++++++++++++++++++++++ src/{ => src}/extras/PwmPin.cpp | 0 src/src/extras/PwmPin.h | 101 +++++++++++ src/{ => src}/extras/RFControl.cpp | 0 src/src/extras/RFControl.h | 76 +++++++++ src/{ => src}/extras/extras.ino | 0 14 files changed, 573 insertions(+), 452 deletions(-) rename src/{ => src}/extras/Blinker.cpp (100%) create mode 100644 src/src/extras/Blinker.h rename src/{ => src}/extras/Pixel.cpp (100%) create mode 100644 src/src/extras/Pixel.h rename src/{ => src}/extras/PwmPin.cpp (100%) create mode 100644 src/src/extras/PwmPin.h rename src/{ => src}/extras/RFControl.cpp (100%) create mode 100644 src/src/extras/RFControl.h rename src/{ => src}/extras/extras.ino (100%) diff --git a/src/extras/Blinker.h b/src/extras/Blinker.h index 633bc19..b54c4ce 100644 --- a/src/extras/Blinker.h +++ b/src/extras/Blinker.h @@ -25,104 +25,6 @@ * ********************************************************************************/ -#pragma once +#include "../src/extras/Blinker.h" -#include -#include -//////////////////////////////// -// Blinkable Interface // -//////////////////////////////// - -class Blinkable { - public: - - virtual void on()=0; - virtual void off()=0; - virtual int getPin()=0; -}; - -//////////////////////////////// -// Blinker // -//////////////////////////////// - -class Blinker { - - TaskHandle_t blinkHandle = NULL; - Blinkable *led; - - int nBlinks; - int onTime; - int offTime; - int delayTime; - - unsigned long pauseDuration; - unsigned long pauseTime; - boolean isPaused=false; - - static void blinkTask(void *arg); - - public: - - Blinker(Blinkable *led, uint16_t autoOffDuration=0); - -// Creates a generic blinking LED in a separate task thread -// -// led: An initialized LED device that implements the Blinkable Interface -//// -// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed -// Blinker will resume normal operation upon next call to start(), on(), or off() -// Program must periodically call check() for auto-off functionality to work - - void start(int period, float dutyCycle=0.5); - -// Starts simple ON/OFF blinking. -// -// period: ON/OFF blinking period, in milliseconds -// dutyCycle: Fraction of period that LED is ON (default=50%) - - void start(int period, float dutyCycle, int nBlinks, int delayTime); - -// Starts ON/OFF blinking pattern. -// -// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern -// dutyCycle: Fraction of period that LED is ON (default=50%) -// nBlinks: Number of blinks in blinking portion of pattern -// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern - - void stop(); - -// Stops current blinking pattern. - - void on(); - -// Stops current blinking pattern and turns on LED - - void off(); - -// Stops current blinking pattern and turns off LED - - void check(); - -// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) - - int getPin(); - -// Returns pin number of connected LED - -}; - -//////////////////////////////// -// GenericLED // -//////////////////////////////// - -class GenericLED : public Blinkable { - int pin; - - public: - - GenericLED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} - void on() {digitalWrite(pin,HIGH);} - void off() {digitalWrite(pin,LOW);} - int getPin() {return(pin);} -}; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index e781129..3bcc8bd 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -25,235 +25,5 @@ * ********************************************************************************/ -//////////////////////////////////////////// -// Addressable LEDs // -//////////////////////////////////////////// +#include "../src/extras/Pixel.h" -#pragma once - -#include "RFControl.h" -#include "PwmPin.h" -#include "Blinker.h" - -//////////////////////////////////////////// -// Single-Wire RGB/RGBW NeoPixels // -//////////////////////////////////////////// - -class Pixel : public Blinkable { - - public: - struct Color { - union{ - struct { - uint8_t white:8; - uint8_t blue:8; - uint8_t red:8; - uint8_t green:8; - }; - uint32_t val; - }; - - Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){ // returns Color based on provided RGB(W) values where r/g/b/w=[0-255] - this->red=r; - this->green=g; - this->blue=b; - this->white=w; - return(*this); - } - - Color HSV(float h, float s, float v, double w=0){ // returns Color based on provided HSV(W) values where h=[0,360] and s/v/w=[0,100] - float r,g,b; - LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - this->red=r*255; - this->green=g*255; - this->blue=b*255; - this->white=w*2.555; - return(*this); - } - - bool operator==(const Color& color){ - return(val==color.val); - } - - bool operator!=(const Color& color){ - return(val!=color.val); - } - - Color operator+(const Color& color){ - Color newColor; - newColor.white=white+color.white; - newColor.blue=blue+color.blue; - newColor.red=red+color.red; - newColor.green=green+color.green; - return(newColor); - } - - Color& operator+=(const Color& color){ - white+=color.white; - red+=color.red; - blue+=color.blue; - green+=color.green; - return(*this); - } - - Color operator-(const Color& color){ - Color newColor; - newColor.white=white-color.white; - newColor.blue=blue-color.blue; - newColor.red=red-color.red; - newColor.green=green-color.green; - return(newColor); - } - - Color& operator-=(const Color& color){ - white-=color.white; - red-=color.red; - blue-=color.blue; - green-=color.green; - return(*this); - } - - }; // Color - - private: - struct pixel_status_t { - int nPixels; - Color *color; - int iBit; - int iMem; - boolean started; - Pixel *px; - boolean multiColor; - }; - - RFControl *rf; // Pixel utilizes RFControl - uint32_t pattern[2]; // storage for zero-bit and one-bit pulses - uint32_t resetTime; // minimum time (in usec) between pulse trains - uint32_t txEndMask; // mask for end-of-transmission interrupt - uint32_t txThrMask; // mask for threshold interrupt - uint32_t lastBit; // 0=RGBW; 8=RGB - Color onColor; // color used for on() command - - const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel - - static void loadData(void *arg); // interrupt handler - volatile static pixel_status_t status; // storage for volatile information modified in interupt handler - - public: - Pixel(int pin, boolean isRGBW=false); // creates addressable single-wire RGB (false) or RGBW (true) LED connected to pin (such as the SK68 or WS28) - void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels - void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c - - static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){return(Color().RGB(r,g,b,w));} // an alternative method for returning an RGB Color - static Color HSV(float h, float s, float v, double w=0){return(Color().HSV(h,s,v,w));} // an alternative method for returning an HSV Color - - int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 - void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS - - operator bool(){ // override boolean operator to return true/false if creation succeeded/failed - return(*rf); - } - - void on() {set(onColor);} - void off() {set(RGB(0,0,0,0));} - Pixel *setOnColor(Color c){onColor=c;return(this);} -}; - -//////////////////////////////////////////// -// Two-Wire RGB DotStars // -//////////////////////////////////////////// - -class Dot { - - public: - struct Color { - union{ - struct { - uint8_t red:8; - uint8_t green:8; - uint8_t blue:8; - uint8_t drive:5; - uint8_t flags:3; - }; - uint32_t val; - }; - - Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){ // returns Color based on provided RGB values where r/g/b=[0-255] and current-limiting drive level=[0,31] - this->red=r; - this->green=g; - this->blue=b; - this->drive=driveLevel; - this->flags=7; - return(*this); - } - - Color HSV(float h, float s, float v, double drivePercent=100){ // returns Color based on provided HSV values where h=[0,360], s/v=[0,100], and current-limiting drive percent=[0,100] - float r,g,b; - LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - this->red=r*255; - this->green=g*255; - this->blue=b*255; - this->drive=drivePercent*0.315; - this->flags=7; - return(*this); - } - - bool operator==(const Color& color){ - return(val==color.val); - } - - bool operator!=(const Color& color){ - return(val!=color.val); - } - - Color operator+(const Color& color){ - Color newColor; - newColor.blue=blue+color.blue; - newColor.red=red+color.red; - newColor.green=green+color.green; - return(newColor); - } - - Color& operator+=(const Color& color){ - red+=color.red; - blue+=color.blue; - green+=color.green; - return(*this); - } - - Color operator-(const Color& color){ - Color newColor; - newColor.blue=blue-color.blue; - newColor.red=red-color.red; - newColor.green=green-color.green; - return(newColor); - } - - Color& operator-=(const Color& color){ - red-=color.red; - blue-=color.blue; - green-=color.green; - return(*this); - } - - }; - - private: - uint32_t dataMask; - uint32_t clockMask; - volatile uint32_t *dataSetReg; - volatile uint32_t *dataClearReg; - volatile uint32_t *clockSetReg; - volatile uint32_t *clockClearReg; - - public: - Dot(uint8_t dataPin, uint8_t clockPin); // creates addressable two-wire RGB LED connected to dataPin and clockPin (such as the DotStar SK9822 or APA102) - void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels - void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c - - static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){return(Color().RGB(r,g,b,driveLevel));} // an alternative method for returning an RGB Color - static Color HSV(float h, float s, float v, double drivePercent=100){return(Color().HSV(h,s,v,drivePercent));} // an alternative method for returning an HSV Color - -}; - -//////////////////////////////////////////// diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index d9506ec..f012652 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -25,77 +25,5 @@ * ********************************************************************************/ -///////////////////////////////////////////////////////////////////////////////////////////////////////////// -// ----- PWM Pin Control ----- -///////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Wrappers around the ESP-IDF ledc library to control PWM-based devices: -// -// LedPin(pin) - controls a Dimmable LED on specified pin with frequency=5000 Hz -// - use set(level) to set brightness from 0-100% -// -// ServoPin(pin) - controls a Servo Motor on specified pin with frequency=50 Hz -// - use set(degrees) to set position to degrees -// -// -///////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../src/extras/PwmPin.h" -#pragma once - -#include -#include -#include "Blinker.h" - -#define DEFAULT_PWM_FREQ 5000 - -///////////////////////////////////// - -class LedC { - - protected: - static ledc_channel_config_t *channelList[LEDC_CHANNEL_MAX][LEDC_SPEED_MODE_MAX]; - static ledc_timer_config_t *timerList[LEDC_TIMER_MAX][LEDC_SPEED_MODE_MAX]; - - ledc_channel_config_t *channel=NULL; - ledc_timer_config_t *timer; - - LedC(uint8_t pin, uint16_t freq); - - public: - int getPin(){return(channel?channel->gpio_num:-1);} // returns the pin number - - operator bool(){ // override boolean operator to return true/false if creation succeeded/failed - return(channel); - } - -}; - -///////////////////////////////////// - -class LedPin : public LedC { - - public: - LedPin(uint8_t pin, float level=0, uint16_t freq=DEFAULT_PWM_FREQ); // assigns pin to be output of one of 16 PWM channels initial level and frequency - void set(float level); // sets the PWM duty to level (0-100) - - static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B -}; - - - -//////////////////////////////// -// ServoPin // -//////////////////////////////// - -class ServoPin : public LedC { - uint16_t minMicros; - uint16_t maxMicros; - double minDegrees; - double microsPerDegree; - - public: - ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees); - ServoPin(uint8_t pin, double initDegrees=0) : ServoPin(pin,initDegrees,1000,2000,-90,90) {}; - - void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees] -}; diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index 34cceb2..bfbcaea 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -24,53 +24,6 @@ * SOFTWARE. * ********************************************************************************/ + +#include "../src/extras/RFControl.h" -//////////////////////////////////// -// RF Control Module // -//////////////////////////////////// - -#pragma once - -#include -#include -#include "driver/rmt.h" -#include - -using std::vector; - -class RFControl { - friend class Pixel; - - private: - rmt_config_t *config=NULL; - vector data; - boolean lowWord=true; - boolean refClock; - static uint8_t nChannels; - - RFControl(uint8_t pin, boolean refClock, boolean installDriver); // private constructor (only used by Pixel class) - - public: - RFControl(uint8_t pin, boolean refClock=true):RFControl(pin,refClock,true){}; // public constructor to create transmitter on pin, using 1-MHz Ref Tick clock or 80-MHz APB clock - - void start(uint32_t *data, int nData, uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from specified data pointer, repeated for numCycles, where each tick in pulse is tickTime microseconds long - void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long - - void clear(); // clears transmitter memory - void add(uint32_t onTime, uint32_t offTime); // adds pulse of onTime ticks HIGH followed by offTime ticks LOW - void phase(uint32_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks - void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1 - void disableCarrier(){enableCarrier(0);} // disables carrier wave - - int getPin(){return(config?config->gpio_num:-1);} // returns the pin number, or -1 if no channel defined - rmt_channel_t getChannel(){return(config?config->channel:RMT_CHANNEL_0);} // returns channel, or channel_0 is no channel defined - - operator bool(){ // override boolean operator to return true/false if creation succeeded/failed - return(config); - } -}; - -// Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above - -#define RF_PULSE(highTicks,lowTicks) (1 << 15 | uint32_t(highTicks) | uint32_t(lowTicks) << 16) - diff --git a/src/src.ino b/src/src.ino index a0ba37e..892c050 100644 --- a/src/src.ino +++ b/src/src.ino @@ -5,6 +5,10 @@ #include "HomeSpan.h" #include "FeatherPins.h" #include "extras/Pixel.h" +#include "extras/RFControl.h" +#include "extras/Blinker.h" +#include "extras/PwmPin.h" + #define STRING_t const char * // WORK-AROUND diff --git a/src/extras/Blinker.cpp b/src/src/extras/Blinker.cpp similarity index 100% rename from src/extras/Blinker.cpp rename to src/src/extras/Blinker.cpp diff --git a/src/src/extras/Blinker.h b/src/src/extras/Blinker.h new file mode 100644 index 0000000..633bc19 --- /dev/null +++ b/src/src/extras/Blinker.h @@ -0,0 +1,128 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#pragma once + +#include +#include + +//////////////////////////////// +// Blinkable Interface // +//////////////////////////////// + +class Blinkable { + public: + + virtual void on()=0; + virtual void off()=0; + virtual int getPin()=0; +}; + +//////////////////////////////// +// Blinker // +//////////////////////////////// + +class Blinker { + + TaskHandle_t blinkHandle = NULL; + Blinkable *led; + + int nBlinks; + int onTime; + int offTime; + int delayTime; + + unsigned long pauseDuration; + unsigned long pauseTime; + boolean isPaused=false; + + static void blinkTask(void *arg); + + public: + + Blinker(Blinkable *led, uint16_t autoOffDuration=0); + +// Creates a generic blinking LED in a separate task thread +// +// led: An initialized LED device that implements the Blinkable Interface +//// +// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed +// Blinker will resume normal operation upon next call to start(), on(), or off() +// Program must periodically call check() for auto-off functionality to work + + void start(int period, float dutyCycle=0.5); + +// Starts simple ON/OFF blinking. +// +// period: ON/OFF blinking period, in milliseconds +// dutyCycle: Fraction of period that LED is ON (default=50%) + + void start(int period, float dutyCycle, int nBlinks, int delayTime); + +// Starts ON/OFF blinking pattern. +// +// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern +// dutyCycle: Fraction of period that LED is ON (default=50%) +// nBlinks: Number of blinks in blinking portion of pattern +// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern + + void stop(); + +// Stops current blinking pattern. + + void on(); + +// Stops current blinking pattern and turns on LED + + void off(); + +// Stops current blinking pattern and turns off LED + + void check(); + +// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) + + int getPin(); + +// Returns pin number of connected LED + +}; + +//////////////////////////////// +// GenericLED // +//////////////////////////////// + +class GenericLED : public Blinkable { + int pin; + + public: + + GenericLED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} + void on() {digitalWrite(pin,HIGH);} + void off() {digitalWrite(pin,LOW);} + int getPin() {return(pin);} +}; diff --git a/src/extras/Pixel.cpp b/src/src/extras/Pixel.cpp similarity index 100% rename from src/extras/Pixel.cpp rename to src/src/extras/Pixel.cpp diff --git a/src/src/extras/Pixel.h b/src/src/extras/Pixel.h new file mode 100644 index 0000000..e781129 --- /dev/null +++ b/src/src/extras/Pixel.h @@ -0,0 +1,259 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////////////// +// Addressable LEDs // +//////////////////////////////////////////// + +#pragma once + +#include "RFControl.h" +#include "PwmPin.h" +#include "Blinker.h" + +//////////////////////////////////////////// +// Single-Wire RGB/RGBW NeoPixels // +//////////////////////////////////////////// + +class Pixel : public Blinkable { + + public: + struct Color { + union{ + struct { + uint8_t white:8; + uint8_t blue:8; + uint8_t red:8; + uint8_t green:8; + }; + uint32_t val; + }; + + Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){ // returns Color based on provided RGB(W) values where r/g/b/w=[0-255] + this->red=r; + this->green=g; + this->blue=b; + this->white=w; + return(*this); + } + + Color HSV(float h, float s, float v, double w=0){ // returns Color based on provided HSV(W) values where h=[0,360] and s/v/w=[0,100] + float r,g,b; + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); + this->red=r*255; + this->green=g*255; + this->blue=b*255; + this->white=w*2.555; + return(*this); + } + + bool operator==(const Color& color){ + return(val==color.val); + } + + bool operator!=(const Color& color){ + return(val!=color.val); + } + + Color operator+(const Color& color){ + Color newColor; + newColor.white=white+color.white; + newColor.blue=blue+color.blue; + newColor.red=red+color.red; + newColor.green=green+color.green; + return(newColor); + } + + Color& operator+=(const Color& color){ + white+=color.white; + red+=color.red; + blue+=color.blue; + green+=color.green; + return(*this); + } + + Color operator-(const Color& color){ + Color newColor; + newColor.white=white-color.white; + newColor.blue=blue-color.blue; + newColor.red=red-color.red; + newColor.green=green-color.green; + return(newColor); + } + + Color& operator-=(const Color& color){ + white-=color.white; + red-=color.red; + blue-=color.blue; + green-=color.green; + return(*this); + } + + }; // Color + + private: + struct pixel_status_t { + int nPixels; + Color *color; + int iBit; + int iMem; + boolean started; + Pixel *px; + boolean multiColor; + }; + + RFControl *rf; // Pixel utilizes RFControl + uint32_t pattern[2]; // storage for zero-bit and one-bit pulses + uint32_t resetTime; // minimum time (in usec) between pulse trains + uint32_t txEndMask; // mask for end-of-transmission interrupt + uint32_t txThrMask; // mask for threshold interrupt + uint32_t lastBit; // 0=RGBW; 8=RGB + Color onColor; // color used for on() command + + const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel + + static void loadData(void *arg); // interrupt handler + volatile static pixel_status_t status; // storage for volatile information modified in interupt handler + + public: + Pixel(int pin, boolean isRGBW=false); // creates addressable single-wire RGB (false) or RGBW (true) LED connected to pin (such as the SK68 or WS28) + void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels + void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c + + static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){return(Color().RGB(r,g,b,w));} // an alternative method for returning an RGB Color + static Color HSV(float h, float s, float v, double w=0){return(Color().HSV(h,s,v,w));} // an alternative method for returning an HSV Color + + int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 + void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS + + operator bool(){ // override boolean operator to return true/false if creation succeeded/failed + return(*rf); + } + + void on() {set(onColor);} + void off() {set(RGB(0,0,0,0));} + Pixel *setOnColor(Color c){onColor=c;return(this);} +}; + +//////////////////////////////////////////// +// Two-Wire RGB DotStars // +//////////////////////////////////////////// + +class Dot { + + public: + struct Color { + union{ + struct { + uint8_t red:8; + uint8_t green:8; + uint8_t blue:8; + uint8_t drive:5; + uint8_t flags:3; + }; + uint32_t val; + }; + + Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){ // returns Color based on provided RGB values where r/g/b=[0-255] and current-limiting drive level=[0,31] + this->red=r; + this->green=g; + this->blue=b; + this->drive=driveLevel; + this->flags=7; + return(*this); + } + + Color HSV(float h, float s, float v, double drivePercent=100){ // returns Color based on provided HSV values where h=[0,360], s/v=[0,100], and current-limiting drive percent=[0,100] + float r,g,b; + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); + this->red=r*255; + this->green=g*255; + this->blue=b*255; + this->drive=drivePercent*0.315; + this->flags=7; + return(*this); + } + + bool operator==(const Color& color){ + return(val==color.val); + } + + bool operator!=(const Color& color){ + return(val!=color.val); + } + + Color operator+(const Color& color){ + Color newColor; + newColor.blue=blue+color.blue; + newColor.red=red+color.red; + newColor.green=green+color.green; + return(newColor); + } + + Color& operator+=(const Color& color){ + red+=color.red; + blue+=color.blue; + green+=color.green; + return(*this); + } + + Color operator-(const Color& color){ + Color newColor; + newColor.blue=blue-color.blue; + newColor.red=red-color.red; + newColor.green=green-color.green; + return(newColor); + } + + Color& operator-=(const Color& color){ + red-=color.red; + blue-=color.blue; + green-=color.green; + return(*this); + } + + }; + + private: + uint32_t dataMask; + uint32_t clockMask; + volatile uint32_t *dataSetReg; + volatile uint32_t *dataClearReg; + volatile uint32_t *clockSetReg; + volatile uint32_t *clockClearReg; + + public: + Dot(uint8_t dataPin, uint8_t clockPin); // creates addressable two-wire RGB LED connected to dataPin and clockPin (such as the DotStar SK9822 or APA102) + void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels + void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c + + static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){return(Color().RGB(r,g,b,driveLevel));} // an alternative method for returning an RGB Color + static Color HSV(float h, float s, float v, double drivePercent=100){return(Color().HSV(h,s,v,drivePercent));} // an alternative method for returning an HSV Color + +}; + +//////////////////////////////////////////// diff --git a/src/extras/PwmPin.cpp b/src/src/extras/PwmPin.cpp similarity index 100% rename from src/extras/PwmPin.cpp rename to src/src/extras/PwmPin.cpp diff --git a/src/src/extras/PwmPin.h b/src/src/extras/PwmPin.h new file mode 100644 index 0000000..d9506ec --- /dev/null +++ b/src/src/extras/PwmPin.h @@ -0,0 +1,101 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ----- PWM Pin Control ----- +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Wrappers around the ESP-IDF ledc library to control PWM-based devices: +// +// LedPin(pin) - controls a Dimmable LED on specified pin with frequency=5000 Hz +// - use set(level) to set brightness from 0-100% +// +// ServoPin(pin) - controls a Servo Motor on specified pin with frequency=50 Hz +// - use set(degrees) to set position to degrees +// +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "Blinker.h" + +#define DEFAULT_PWM_FREQ 5000 + +///////////////////////////////////// + +class LedC { + + protected: + static ledc_channel_config_t *channelList[LEDC_CHANNEL_MAX][LEDC_SPEED_MODE_MAX]; + static ledc_timer_config_t *timerList[LEDC_TIMER_MAX][LEDC_SPEED_MODE_MAX]; + + ledc_channel_config_t *channel=NULL; + ledc_timer_config_t *timer; + + LedC(uint8_t pin, uint16_t freq); + + public: + int getPin(){return(channel?channel->gpio_num:-1);} // returns the pin number + + operator bool(){ // override boolean operator to return true/false if creation succeeded/failed + return(channel); + } + +}; + +///////////////////////////////////// + +class LedPin : public LedC { + + public: + LedPin(uint8_t pin, float level=0, uint16_t freq=DEFAULT_PWM_FREQ); // assigns pin to be output of one of 16 PWM channels initial level and frequency + void set(float level); // sets the PWM duty to level (0-100) + + static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B +}; + + + +//////////////////////////////// +// ServoPin // +//////////////////////////////// + +class ServoPin : public LedC { + uint16_t minMicros; + uint16_t maxMicros; + double minDegrees; + double microsPerDegree; + + public: + ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees); + ServoPin(uint8_t pin, double initDegrees=0) : ServoPin(pin,initDegrees,1000,2000,-90,90) {}; + + void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees] +}; diff --git a/src/extras/RFControl.cpp b/src/src/extras/RFControl.cpp similarity index 100% rename from src/extras/RFControl.cpp rename to src/src/extras/RFControl.cpp diff --git a/src/src/extras/RFControl.h b/src/src/extras/RFControl.h new file mode 100644 index 0000000..34cceb2 --- /dev/null +++ b/src/src/extras/RFControl.h @@ -0,0 +1,76 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////// +// RF Control Module // +//////////////////////////////////// + +#pragma once + +#include +#include +#include "driver/rmt.h" +#include + +using std::vector; + +class RFControl { + friend class Pixel; + + private: + rmt_config_t *config=NULL; + vector data; + boolean lowWord=true; + boolean refClock; + static uint8_t nChannels; + + RFControl(uint8_t pin, boolean refClock, boolean installDriver); // private constructor (only used by Pixel class) + + public: + RFControl(uint8_t pin, boolean refClock=true):RFControl(pin,refClock,true){}; // public constructor to create transmitter on pin, using 1-MHz Ref Tick clock or 80-MHz APB clock + + void start(uint32_t *data, int nData, uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from specified data pointer, repeated for numCycles, where each tick in pulse is tickTime microseconds long + void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long + + void clear(); // clears transmitter memory + void add(uint32_t onTime, uint32_t offTime); // adds pulse of onTime ticks HIGH followed by offTime ticks LOW + void phase(uint32_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks + void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1 + void disableCarrier(){enableCarrier(0);} // disables carrier wave + + int getPin(){return(config?config->gpio_num:-1);} // returns the pin number, or -1 if no channel defined + rmt_channel_t getChannel(){return(config?config->channel:RMT_CHANNEL_0);} // returns channel, or channel_0 is no channel defined + + operator bool(){ // override boolean operator to return true/false if creation succeeded/failed + return(config); + } +}; + +// Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above + +#define RF_PULSE(highTicks,lowTicks) (1 << 15 | uint32_t(highTicks) | uint32_t(lowTicks) << 16) + diff --git a/src/extras/extras.ino b/src/src/extras/extras.ino similarity index 100% rename from src/extras/extras.ino rename to src/src/extras/extras.ino From 22cfa130b41a07a07d5cf1d2054ba4a4808ba29e Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 24 Sep 2022 16:49:32 -0500 Subject: [PATCH 13/75] Created HomePeer This is a self-contained library that is used for remote devices set up as HomeSpan Peers via ESP-NOW. --- src/{Now.cpp => HomePeer.cpp} | 25 ++++++++++++++----------- src/{Now.h => HomePeer.h} | 6 +++--- src/HomeSpan.cpp | 5 +++-- src/HomeSpan.h | 1 - 4 files changed, 20 insertions(+), 17 deletions(-) rename src/{Now.cpp => HomePeer.cpp} (60%) rename src/{Now.h => HomePeer.h} (92%) diff --git a/src/Now.cpp b/src/HomePeer.cpp similarity index 60% rename from src/Now.cpp rename to src/HomePeer.cpp index 18ed780..7204b1a 100644 --- a/src/Now.cpp +++ b/src/HomePeer.cpp @@ -1,12 +1,14 @@ -#include "Now.h" +#include "HomePeer.h" + #include #include #include +#include -void SpanNow::start(const char *macAddress, const char *password){ +void SpanPeer::start(const char *macAddress, const char *password){ - if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",mac,mac+1,mac+2,mac+3,mac+4,mac+5)!=6){ + if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ Serial.printf("*** ERROR: Can't start HomeSpan NOW! Bad MAC Address '%s'\n\n",macAddress); return; } @@ -17,26 +19,27 @@ void SpanNow::start(const char *macAddress, const char *password){ esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); esp_now_init(); esp_now_register_send_cb(onDataSent); - + uint8_t lmk[32]; + uint8_t mac[6]; mbedtls_sha256_ret((const unsigned char *)password,strlen(password),lmk,0); esp_now_set_pmk(lmk+16); - memcpy(peerInfo.peer_addr, mac, 6); peerInfo.encrypt = true; memcpy(peerInfo.lmk, lmk, 16); esp_now_add_peer(&peerInfo); esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); - Serial.printf("Started HomeSpan NOW on Channel=%d. Hub MAC Address: %X:%X:%X:%X:%X:%X\n",channel,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); + Serial.printf("Started HomePeer: MAC Address = %s HomeSpan Address = %X:%X:%X:%X:%X:%X\n",WiFi.macAddress().c_str(), + peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5]); started=true; } -boolean SpanNow::send(uint8_t *data, size_t len){ +boolean SpanPeer::send(uint8_t *data, size_t len){ if(!started){ - Serial.printf("*** ERROR: Can't send data until HomeSpanNOW has been started.\n\n"); + Serial.printf("*** ERROR: Can't send data until HomePeer has been started.\n\n"); return(false); } @@ -46,7 +49,7 @@ boolean SpanNow::send(uint8_t *data, size_t len){ if((1< #include - -class SpanNow { +class SpanPeer { esp_now_peer_info_t peerInfo; - uint8_t mac[6]; boolean started=false; static QueueHandle_t statusQueue; int channel=1; @@ -23,3 +21,5 @@ class SpanNow { boolean send(uint8_t *data, size_t len); }; + +extern SpanPeer homePeer; diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 947ffaa..70a9086 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -58,6 +58,8 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa this->modelName=modelName; sprintf(this->category,"%d",(int)catID); + WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) + statusLED=new Blinker(statusDevice,autoOffLED); // create Status LED, even is statusDevice is NULL esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); // required to avoid watchdog timeout messages from ESP32-C3 @@ -138,9 +140,8 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa Serial.print(__TIME__); Serial.printf("\nPartition: %s",esp_ota_get_running_partition()->label); - + if(espNowEnabled){ - WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. AP mode will not be started, but WiFi will be properly configured for use with ESP-NOW esp_now_init(); // initialize ESP NOW Serial.print("\nESP-NOW: ENABLED"); } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 02ac360..205aac8 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -48,7 +48,6 @@ #include "HAPConstants.h" #include "HapQR.h" #include "Characteristics.h" -#include "Now.h" using std::vector; using std::unordered_map; From 4f983051ce7b14a47cdafb0225c1ebf3a659af54 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 25 Sep 2022 13:53:54 -0500 Subject: [PATCH 14/75] Created SpanPoint Verified ESP-NOW is communicating correctly with HomeSpan. Verified encryption works. Next step - create linkages between incoming data and Services; create queue structure to transfer data. --- src/HomeSpan.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++------ src/HomeSpan.h | 29 ++++++++++++++++++---- src/src.ino | 3 +++ 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 70a9086..c77ed3f 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -31,10 +31,10 @@ #include #include #include +#include #include #include #include -#include #include "HomeSpan.h" #include "HAP.h" @@ -140,11 +140,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa Serial.print(__TIME__); Serial.printf("\nPartition: %s",esp_ota_get_running_partition()->label); - - if(espNowEnabled){ - esp_now_init(); // initialize ESP NOW - Serial.print("\nESP-NOW: ENABLED"); - } + Serial.printf("\nMAC Address: %s",WiFi.macAddress().c_str()); Serial.print("\n\nDevice Name: "); Serial.print(displayName); @@ -449,7 +445,6 @@ void Span::checkConnect(){ connected++; addWebLog(true,"WiFi Connected! IP Address = %s",WiFi.localIP().toString().c_str()); - Serial.printf("MAC Address = %s, Channel = %d\n",WiFi.macAddress().c_str(),WiFi.channel()); if(connected>1) // Do not initialize everything below if this is only a reconnect return; @@ -2155,6 +2150,57 @@ void SpanOTA::error(ota_error_t err){ else if (err == OTA_END_ERROR) Serial.println("End Failed\n"); } +/////////////////////////////// +// SpanPoint // +/////////////////////////////// + +SpanPoint::SpanPoint(const char *macAddress){ + + if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){ + Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") without a defined Service ***\n",macAddress); + Serial.printf("\n=== PROGRAM HALTED ==="); + while(1); + } + + init(); + + if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ + Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress); + Serial.printf("\n=== PROGRAM HALTED ==="); + while(1); + } + + peerInfo.channel=0; // 0=matches current WiFi channel + peerInfo.ifidx=WIFI_IF_STA; // must specify interface + peerInfo.encrypt=true; // turn on encryption for this peer + memcpy(peerInfo.lmk, lmk, 16); // set local key + esp_now_add_peer(&peerInfo); // add peer to ESP-NOW +} + +/////////////////////////////// + +void SpanPoint::init(const char *password){ + + if(initialized) + return; + + uint8_t hash[32]; + mbedtls_sha256_ret((const unsigned char *)password,strlen(password),hash,0); // produce 256-bit bit hash from password + + esp_now_init(); // initialize ESP-NOW + memcpy(lmk, hash, 16); // store first 16 bytes of hash for later use as local key + esp_now_set_pmk(hash+16); // set hash for primary key using last 16 bytes of hash + esp_now_register_recv_cb(dataReceived); // set callback for receiving data + + initialized=true; +} + +/////////////////////////////// + +void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len){ + Serial.printf("SpanPoint: %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); +} + /////////////////////////////// void __attribute__((weak)) loop(){ @@ -2166,5 +2212,7 @@ int SpanOTA::otaPercent; boolean SpanOTA::safeLoad; boolean SpanOTA::enabled=false; boolean SpanOTA::auth; +uint8_t SpanPoint::lmk[16]; +boolean SpanPoint::initialized=false; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 205aac8..75ed259 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "extras/Blinker.h" #include "extras/Pixel.h" @@ -155,6 +156,25 @@ struct SpanOTA{ // manages OTA process // USER API CLASSES BEGINS HERE // ////////////////////////////////////// +class SpanPoint { + + friend class Span; + + esp_now_peer_info_t peerInfo; + + static uint8_t lmk[16]; + static boolean initialized; + + static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); + static void init(const char *password="HomeSpan"); + + public: + + SpanPoint(const char *macAddress); +}; + +/////////////////////////////// + class Span{ friend class SpanAccessory; @@ -167,6 +187,7 @@ class Span{ friend class SpanOTA; friend class Network; friend class HAPClient; + friend class SpanPoint; const char *displayName; // display name for this device - broadcast as part of Bonjour MDNS const char *hostNameBase; // base of hostName of this device - full host name broadcast by Bonjour MDNS will have 6-byte accessoryID as well as '.local' automatically appended @@ -185,7 +206,6 @@ class Span{ char pairingCodeCommand[12]=""; // user-specified Pairing Code - only needed if Pairing Setup Code is specified in sketch using setPairingCode() String lastClientIP="0.0.0.0"; // IP address of last client accessing device through encrypted channel boolean newCode; // flag indicating new application code has been loaded (based on keeping track of app SHA256) - boolean espNowEnabled=false; // flag indicating if ESP-NOW is enabled int connected=0; // WiFi connection status (increments upon each connect and disconnect) unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts @@ -284,8 +304,8 @@ class Span{ void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials - void enableEspNOW(){espNowEnabled=true;}; // enables ESP-NOW - + void setSpanPointPassword(const char *pwd){SpanPoint::init(pwd);}; // sets SpanPoint password + void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS @@ -305,7 +325,7 @@ class Span{ void autoPoll(uint32_t stackSize=CONFIG_ARDUINO_LOOP_STACK_SIZE){xTaskCreateUniversal([](void *parms){for(;;)homeSpan.pollTask();}, "pollTask", stackSize, NULL, 1, &pollTaskHandle, 0);} // start pollTask() void setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;} // sets wait time (in seconds) for optional web log time server to connect - + [[deprecated("Please use reserveSocketConnections(n) method instead.")]] void setMaxConnections(uint8_t n){requestedMaxCon=n;} // sets maximum number of simultaneous HAP connections }; @@ -319,6 +339,7 @@ class SpanAccessory{ friend class SpanCharacteristic; friend class SpanButton; friend class SpanRange; + friend class SpanPoint; uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1) int iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory diff --git a/src/src.ino b/src/src.ino index 892c050..11f4331 100644 --- a/src/src.ino +++ b/src/src.ino @@ -42,6 +42,8 @@ void setup() { homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); + homeSpan.setSpanPointPassword("Hello Thert"); + new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics @@ -63,6 +65,7 @@ void setup() { new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); new Characteristic::Active(); + new SpanPoint("AC:67:B2:77:42:20"); new Service::LightBulb(); new Characteristic::On(0,true); (new Characteristic::Brightness(50,false))->setRange(10,100,5); From 58683586bc1bc24dc4f0f8fe0863e17b264e6462 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 26 Sep 2022 22:00:32 -0500 Subject: [PATCH 15/75] Added SpanPoint::get() This completes SpanPoint. Next: rename HomePeer to be HomePoint. --- src/HomePeer.cpp | 2 ++ src/HomeSpan.cpp | 43 ++++++++++++++++++++++++++++++++----------- src/HomeSpan.h | 11 +++++++---- src/src.ino | 20 +++++++++++++++++++- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/HomePeer.cpp b/src/HomePeer.cpp index 7204b1a..b50f155 100644 --- a/src/HomePeer.cpp +++ b/src/HomePeer.cpp @@ -26,6 +26,8 @@ void SpanPeer::start(const char *macAddress, const char *password){ mbedtls_sha256_ret((const unsigned char *)password,strlen(password),lmk,0); esp_now_set_pmk(lmk+16); + peerInfo.channel=0; + peerInfo.ifidx=WIFI_IF_STA; peerInfo.encrypt = true; memcpy(peerInfo.lmk, lmk, 16); esp_now_add_peer(&peerInfo); diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index c77ed3f..07873d1 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -2154,15 +2154,8 @@ void SpanOTA::error(ota_error_t err){ // SpanPoint // /////////////////////////////// -SpanPoint::SpanPoint(const char *macAddress){ +SpanPoint::SpanPoint(const char *macAddress, int qLength, int nItems){ - if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){ - Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") without a defined Service ***\n",macAddress); - Serial.printf("\n=== PROGRAM HALTED ==="); - while(1); - } - - init(); if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress); @@ -2170,11 +2163,17 @@ SpanPoint::SpanPoint(const char *macAddress){ while(1); } - peerInfo.channel=0; // 0=matches current WiFi channel + init(); // initialize SpanPoint + peerInfo.channel=0; // 0 = matches current WiFi channel peerInfo.ifidx=WIFI_IF_STA; // must specify interface peerInfo.encrypt=true; // turn on encryption for this peer memcpy(peerInfo.lmk, lmk, 16); // set local key esp_now_add_peer(&peerInfo); // add peer to ESP-NOW + + this->qLength=qLength; + dataQueue = xQueueCreate(nItems,qLength); + + SpanPoints.push_back(this); } /////////////////////////////// @@ -2197,12 +2196,33 @@ void SpanPoint::init(const char *password){ /////////////////////////////// -void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len){ - Serial.printf("SpanPoint: %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); +boolean SpanPoint::get(void *dataBuf){ + + return(xQueueReceive(dataQueue, dataBuf, 0)); } /////////////////////////////// +void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len){ + + auto it=SpanPoints.begin(); + for(;it!=SpanPoints.end() && memcmp((*it)->peerInfo.peer_addr,mac,6)!=0; it++); + + if(it==SpanPoints.end()) + return; + + if(len!=(*it)->qLength){ + Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->qLength); + return; + } + + xQueueSend((*it)->dataQueue, incomingData, pdMS_TO_TICKS(1000)); +} + +/////////////////////////////// +// MISC // +/////////////////////////////// + void __attribute__((weak)) loop(){ } @@ -2214,5 +2234,6 @@ boolean SpanOTA::enabled=false; boolean SpanOTA::auth; uint8_t SpanPoint::lmk[16]; boolean SpanPoint::initialized=false; +vector SpanPoint::SpanPoints; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 75ed259..57f358d 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -160,17 +160,21 @@ class SpanPoint { friend class Span; - esp_now_peer_info_t peerInfo; - + int qLength; // length of 1 queue item (in bytes) + esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data + QueueHandle_t dataQueue; // queue to store data after it is received + static uint8_t lmk[16]; static boolean initialized; + static vector SpanPoints; static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); static void init(const char *password="HomeSpan"); public: - SpanPoint(const char *macAddress); + SpanPoint(const char *macAddress, int qLength, int nItems=1); + boolean get(void *dataBuf); }; /////////////////////////////// @@ -339,7 +343,6 @@ class SpanAccessory{ friend class SpanCharacteristic; friend class SpanButton; friend class SpanRange; - friend class SpanPoint; uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1) int iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory diff --git a/src/src.ino b/src/src.ino index 11f4331..16c3da2 100644 --- a/src/src.ino +++ b/src/src.ino @@ -15,6 +15,16 @@ CUSTOM_CHAR(LightMode, AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA, PR, STRING, "ANY_VALUE", NULL, NULL, true); CUSTOM_CHAR_STRING(DarkMode, AAAAAAAA-BBBB-AAAA-AAAA-AAAAAAAAAAAA, PR, "MY_VALUE"); +SpanPoint *dev1; +SpanPoint *dev2; + +struct message_t { + char a[32]; + int b; + float c; + bool d; +} message; + void setup() { Serial.begin(115200); @@ -44,6 +54,9 @@ void setup() { homeSpan.setSpanPointPassword("Hello Thert"); + dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(message_t)); + dev2=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(message_t)); + new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics @@ -65,7 +78,8 @@ void setup() { new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); new Characteristic::Active(); - new SpanPoint("AC:67:B2:77:42:20"); + + new Service::LightBulb(); new Characteristic::On(0,true); (new Characteristic::Brightness(50,false))->setRange(10,100,5); @@ -99,6 +113,10 @@ void setup() { void loop(){ homeSpan.poll(); + if(dev1->get(&message)) + Serial.printf("DEV1: '%s' %d %f %d\n",message.a,message.b,message.c,message.d); + if(dev2->get(&message)) + Serial.printf("DEV2: '%s' %d %f %d\n",message.a,message.b,message.c,message.d); } // end of loop() From 72294bde8b82926a6e53775d18a4ea21d25e8b04 Mon Sep 17 00:00:00 2001 From: Gregg Date: Wed, 28 Sep 2022 21:16:29 -0500 Subject: [PATCH 16/75] Move all SpanPoint code into separate HomePoint.cpp and HomePoint.h files This enables SpanPoint to operate as a standalone library with no reliance on anything in HomeSpan. To Do: migrate all HomePeer code into SpanPoint. This will yield a universal standalone HomePoint library that can be used for BOTH the receiver and the sender (which automatically means two-way communication is always possible) --- src/HomePoint.cpp | 103 ++++++++++++++++++++++++++++++++++++++++++++++ src/HomePoint.h | 54 ++++++++++++++++++++++++ src/HomeSpan.cpp | 76 ++-------------------------------- src/HomeSpan.h | 26 ------------ src/src.ino | 6 ++- 5 files changed, 164 insertions(+), 101 deletions(-) create mode 100644 src/HomePoint.cpp create mode 100644 src/HomePoint.h diff --git a/src/HomePoint.cpp b/src/HomePoint.cpp new file mode 100644 index 0000000..dc771d5 --- /dev/null +++ b/src/HomePoint.cpp @@ -0,0 +1,103 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#include "HomePoint.h" +#include +#include + +SpanPoint::SpanPoint(const char *macAddress, int qLength, int nItems){ + + if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ + Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress); + Serial.printf("\n=== PROGRAM HALTED ==="); + while(1); + } + + init(); // initialize SpanPoint + peerInfo.channel=0; // 0 = matches current WiFi channel + peerInfo.ifidx=WIFI_IF_STA; // must specify interface + peerInfo.encrypt=true; // turn on encryption for this peer + memcpy(peerInfo.lmk, lmk, 16); // set local key + esp_now_add_peer(&peerInfo); // add peer to ESP-NOW + + this->qLength=qLength; + dataQueue = xQueueCreate(nItems,qLength); + + SpanPoints.push_back(this); +} + +/////////////////////////////// + +void SpanPoint::init(const char *password){ + + if(initialized) + return; + + if(WiFi.getMode()!=WIFI_AP_STA) + WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) + + uint8_t hash[32]; + mbedtls_sha256_ret((const unsigned char *)password,strlen(password),hash,0); // produce 256-bit bit hash from password + + esp_now_init(); // initialize ESP-NOW + memcpy(lmk, hash, 16); // store first 16 bytes of hash for later use as local key + esp_now_set_pmk(hash+16); // set hash for primary key using last 16 bytes of hash + esp_now_register_recv_cb(dataReceived); // set callback for receiving data + + initialized=true; +} + +/////////////////////////////// + +boolean SpanPoint::get(void *dataBuf){ + + return(xQueueReceive(dataQueue, dataBuf, 0)); +} + +/////////////////////////////// + +void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len){ + + auto it=SpanPoints.begin(); + for(;it!=SpanPoints.end() && memcmp((*it)->peerInfo.peer_addr,mac,6)!=0; it++); + + if(it==SpanPoints.end()) + return; + + if(len!=(*it)->qLength){ + Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->qLength); + return; + } + + xQueueSend((*it)->dataQueue, incomingData, pdMS_TO_TICKS(1000)); +} + +/////////////////////////////// + +uint8_t SpanPoint::lmk[16]; +boolean SpanPoint::initialized=false; +vector SpanPoint::SpanPoints; diff --git a/src/HomePoint.h b/src/HomePoint.h new file mode 100644 index 0000000..0b1b83a --- /dev/null +++ b/src/HomePoint.h @@ -0,0 +1,54 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#include +#include +#include + +using std::vector; + +class SpanPoint { + + int qLength; // length of 1 queue item (in bytes) + esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data + QueueHandle_t dataQueue; // queue to store data after it is received + + static uint8_t lmk[16]; + static boolean initialized; + static vector SpanPoints; + + static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); + static void init(const char *password="HomeSpan"); + + public: + + SpanPoint(const char *macAddress, int qLength, int nItems=1); + static void setPassword(const char *pwd){init(pwd);}; + boolean get(void *dataBuf); +}; + +/////////////////////////////// diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 07873d1..768b7c3 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -58,7 +58,8 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa this->modelName=modelName; sprintf(this->category,"%d",(int)catID); - WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) + if(WiFi.getMode()!=WIFI_AP_STA) + WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) statusLED=new Blinker(statusDevice,autoOffLED); // create Status LED, even is statusDevice is NULL @@ -2150,75 +2151,6 @@ void SpanOTA::error(ota_error_t err){ else if (err == OTA_END_ERROR) Serial.println("End Failed\n"); } -/////////////////////////////// -// SpanPoint // -/////////////////////////////// - -SpanPoint::SpanPoint(const char *macAddress, int qLength, int nItems){ - - - if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ - Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress); - Serial.printf("\n=== PROGRAM HALTED ==="); - while(1); - } - - init(); // initialize SpanPoint - peerInfo.channel=0; // 0 = matches current WiFi channel - peerInfo.ifidx=WIFI_IF_STA; // must specify interface - peerInfo.encrypt=true; // turn on encryption for this peer - memcpy(peerInfo.lmk, lmk, 16); // set local key - esp_now_add_peer(&peerInfo); // add peer to ESP-NOW - - this->qLength=qLength; - dataQueue = xQueueCreate(nItems,qLength); - - SpanPoints.push_back(this); -} - -/////////////////////////////// - -void SpanPoint::init(const char *password){ - - if(initialized) - return; - - uint8_t hash[32]; - mbedtls_sha256_ret((const unsigned char *)password,strlen(password),hash,0); // produce 256-bit bit hash from password - - esp_now_init(); // initialize ESP-NOW - memcpy(lmk, hash, 16); // store first 16 bytes of hash for later use as local key - esp_now_set_pmk(hash+16); // set hash for primary key using last 16 bytes of hash - esp_now_register_recv_cb(dataReceived); // set callback for receiving data - - initialized=true; -} - -/////////////////////////////// - -boolean SpanPoint::get(void *dataBuf){ - - return(xQueueReceive(dataQueue, dataBuf, 0)); -} - -/////////////////////////////// - -void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len){ - - auto it=SpanPoints.begin(); - for(;it!=SpanPoints.end() && memcmp((*it)->peerInfo.peer_addr,mac,6)!=0; it++); - - if(it==SpanPoints.end()) - return; - - if(len!=(*it)->qLength){ - Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->qLength); - return; - } - - xQueueSend((*it)->dataQueue, incomingData, pdMS_TO_TICKS(1000)); -} - /////////////////////////////// // MISC // /////////////////////////////// @@ -2232,8 +2164,6 @@ int SpanOTA::otaPercent; boolean SpanOTA::safeLoad; boolean SpanOTA::enabled=false; boolean SpanOTA::auth; -uint8_t SpanPoint::lmk[16]; -boolean SpanPoint::initialized=false; -vector SpanPoint::SpanPoints; + diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 57f358d..8d5ec0a 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -39,7 +39,6 @@ #include #include #include -#include #include "extras/Blinker.h" #include "extras/Pixel.h" @@ -156,29 +155,6 @@ struct SpanOTA{ // manages OTA process // USER API CLASSES BEGINS HERE // ////////////////////////////////////// -class SpanPoint { - - friend class Span; - - int qLength; // length of 1 queue item (in bytes) - esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data - QueueHandle_t dataQueue; // queue to store data after it is received - - static uint8_t lmk[16]; - static boolean initialized; - static vector SpanPoints; - - static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); - static void init(const char *password="HomeSpan"); - - public: - - SpanPoint(const char *macAddress, int qLength, int nItems=1); - boolean get(void *dataBuf); -}; - -/////////////////////////////// - class Span{ friend class SpanAccessory; @@ -191,7 +167,6 @@ class Span{ friend class SpanOTA; friend class Network; friend class HAPClient; - friend class SpanPoint; const char *displayName; // display name for this device - broadcast as part of Bonjour MDNS const char *hostNameBase; // base of hostName of this device - full host name broadcast by Bonjour MDNS will have 6-byte accessoryID as well as '.local' automatically appended @@ -308,7 +283,6 @@ class Span{ void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials - void setSpanPointPassword(const char *pwd){SpanPoint::init(pwd);}; // sets SpanPoint password void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS diff --git a/src/src.ino b/src/src.ino index 16c3da2..81e5b81 100644 --- a/src/src.ino +++ b/src/src.ino @@ -8,6 +8,7 @@ #include "extras/RFControl.h" #include "extras/Blinker.h" #include "extras/PwmPin.h" +#include "HomePoint.h" #define STRING_t const char * // WORK-AROUND @@ -50,11 +51,12 @@ void setup() { // homeSpan.enableAutoStartAP(); // homeSpan.setApFunction(myWiFiAP); + SpanPoint::setPassword("Hello Thert"); + dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(message_t)); + homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); - homeSpan.setSpanPointPassword("Hello Thert"); - dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(message_t)); dev2=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(message_t)); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments From c819426dec1af17ea343d286eaec2e6be2446522 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 30 Sep 2022 17:27:57 -0500 Subject: [PATCH 17/75] Moving HomePoint.h back into HomeSpan.h Added SpanPoint::setAsHub(), which is called after homeSpan.begin() to set device as Hub so SpanPoint knows not to change WiFi channel. Also added check to make sure there are no SpanPoint objects instantiated before a call to homeSpan.begin() (if it is called at all). --- src/HomePoint.cpp | 34 +++++++++++++++++++++-------- src/HomePoint.h | 54 ----------------------------------------------- src/HomeSpan.cpp | 2 ++ src/HomeSpan.h | 28 ++++++++++++++++++++++++ src/src.ino | 9 +++----- 5 files changed, 58 insertions(+), 69 deletions(-) delete mode 100644 src/HomePoint.h diff --git a/src/HomePoint.cpp b/src/HomePoint.cpp index dc771d5..b6030bb 100644 --- a/src/HomePoint.cpp +++ b/src/HomePoint.cpp @@ -25,11 +25,10 @@ * ********************************************************************************/ -#include "HomePoint.h" -#include +#include "HomeSpan.h" #include -SpanPoint::SpanPoint(const char *macAddress, int qLength, int nItems){ +SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth){ if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress); @@ -37,6 +36,12 @@ SpanPoint::SpanPoint(const char *macAddress, int qLength, int nItems){ while(1); } + this->sendSize=sendSize; + this->receiveSize=receiveSize; + + Serial.printf("SpanPoint: Created link to device with MAC Address %02X:%02X:%02X:%02X:%02X:%02X. Send size: %d bytes. Receive size: %d bytes (queue depth=%d).\n", + peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5],sendSize,receiveSize,queueDepth); + init(); // initialize SpanPoint peerInfo.channel=0; // 0 = matches current WiFi channel peerInfo.ifidx=WIFI_IF_STA; // must specify interface @@ -44,8 +49,7 @@ SpanPoint::SpanPoint(const char *macAddress, int qLength, int nItems){ memcpy(peerInfo.lmk, lmk, 16); // set local key esp_now_add_peer(&peerInfo); // add peer to ESP-NOW - this->qLength=qLength; - dataQueue = xQueueCreate(nItems,qLength); + receiveQueue = xQueueCreate(queueDepth,receiveSize); SpanPoints.push_back(this); } @@ -75,7 +79,18 @@ void SpanPoint::init(const char *password){ boolean SpanPoint::get(void *dataBuf){ - return(xQueueReceive(dataQueue, dataBuf, 0)); + return(xQueueReceive(receiveQueue, dataBuf, 0)); +} + +/////////////////////////////// + +void SpanPoint::setAsHub(){ + if(SpanPoints.size()>0){ + Serial.printf("\nFATAL ERROR! SpanPoint objects created in main hub device must be instantiated AFTER calling homeSpan.begin() ***\n"); + Serial.printf("\n=== PROGRAM HALTED ==="); + while(1); + } + isHub=true; } /////////////////////////////// @@ -88,16 +103,17 @@ void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, in if(it==SpanPoints.end()) return; - if(len!=(*it)->qLength){ - Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->qLength); + if(len!=(*it)->receiveSize){ + Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->receiveSize); return; } - xQueueSend((*it)->dataQueue, incomingData, pdMS_TO_TICKS(1000)); + xQueueSend((*it)->receiveQueue, incomingData, 0); // send to queue - do not wait if queue is full and instead fail immediately since we need to return from this function ASAP } /////////////////////////////// uint8_t SpanPoint::lmk[16]; boolean SpanPoint::initialized=false; +boolean SpanPoint::isHub=false; vector SpanPoint::SpanPoints; diff --git a/src/HomePoint.h b/src/HomePoint.h deleted file mode 100644 index 0b1b83a..0000000 --- a/src/HomePoint.h +++ /dev/null @@ -1,54 +0,0 @@ -/********************************************************************************* - * MIT License - * - * Copyright (c) 2020-2022 Gregg E. Berman - * - * https://github.com/HomeSpan/HomeSpan - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - ********************************************************************************/ - -#include -#include -#include - -using std::vector; - -class SpanPoint { - - int qLength; // length of 1 queue item (in bytes) - esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data - QueueHandle_t dataQueue; // queue to store data after it is received - - static uint8_t lmk[16]; - static boolean initialized; - static vector SpanPoints; - - static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); - static void init(const char *password="HomeSpan"); - - public: - - SpanPoint(const char *macAddress, int qLength, int nItems=1); - static void setPassword(const char *pwd){init(pwd);}; - boolean get(void *dataBuf); -}; - -/////////////////////////////// diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 768b7c3..e455be5 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -58,6 +58,8 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa this->modelName=modelName; sprintf(this->category,"%d",(int)catID); + SpanPoint::setAsHub(); + if(WiFi.getMode()!=WIFI_AP_STA) WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 8d5ec0a..6f8f7fc 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "extras/Blinker.h" #include "extras/Pixel.h" @@ -749,6 +750,33 @@ class SpanUserCommand { SpanUserCommand(char c, const char *s, void (*f)(const char *, void *), void *arg); }; +/////////////////////////////// + +class SpanPoint { + + friend class Span; + + int receiveSize; // size (in bytes) of messages to receive + int sendSize; // size (in bytes) of messages to send + esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data + QueueHandle_t receiveQueue; // queue to store data after it is received + + static uint8_t lmk[16]; + static boolean initialized; + static boolean isHub; + static vector SpanPoints; + + static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); + static void init(const char *password="HomeSpan"); + static void setAsHub(); + + public: + + SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth=1); + static void setPassword(const char *pwd){init(pwd);}; + boolean get(void *dataBuf); +}; + ///////////////////////////////////////////////// #include "Span.h" diff --git a/src/src.ino b/src/src.ino index 81e5b81..f99ed28 100644 --- a/src/src.ino +++ b/src/src.ino @@ -8,7 +8,6 @@ #include "extras/RFControl.h" #include "extras/Blinker.h" #include "extras/PwmPin.h" -#include "HomePoint.h" #define STRING_t const char * // WORK-AROUND @@ -51,13 +50,11 @@ void setup() { // homeSpan.enableAutoStartAP(); // homeSpan.setApFunction(myWiFiAP); - SpanPoint::setPassword("Hello Thert"); - dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(message_t)); - homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); - - dev2=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(message_t)); + SpanPoint::setPassword("Hello Thert"); + dev1=new SpanPoint("AC:67:B2:77:42:20",0,sizeof(message_t)); + dev2=new SpanPoint("7C:DF:A1:61:E4:A8",0,sizeof(message_t)); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments From b7317c3b5f76b4be1da70343b2dc310ba2cf8293 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 30 Sep 2022 22:20:32 -0500 Subject: [PATCH 18/75] Added SpanPoint::send() functionality Works as expected. Next Up: Modify send() logic so that channels are NOT scanned if device is a main HomeSpan hub. --- src/HomePoint.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++-- src/HomeSpan.cpp | 3 +- src/HomeSpan.h | 11 ++++++- src/src.ino | 10 +++++- 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/src/HomePoint.cpp b/src/HomePoint.cpp index b6030bb..0650100 100644 --- a/src/HomePoint.cpp +++ b/src/HomePoint.cpp @@ -27,6 +27,7 @@ #include "HomeSpan.h" #include +#include SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth){ @@ -36,10 +37,16 @@ SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int while(1); } + if(sendSize<0 || sendSize>200 || receiveSize<0 || receiveSize>200 || queueDepth<1 || (sendSize==0 && receiveSize==0)){ + Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\",%d,%d,%d) - one or more invalid parameters ***\n",macAddress,sendSize,receiveSize,queueDepth); + Serial.printf("\n=== PROGRAM HALTED ==="); + while(1); + } + this->sendSize=sendSize; this->receiveSize=receiveSize; - Serial.printf("SpanPoint: Created link to device with MAC Address %02X:%02X:%02X:%02X:%02X:%02X. Send size: %d bytes. Receive size: %d bytes (queue depth=%d).\n", + Serial.printf("SpanPoint: Created link to device with MAC Address %02X:%02X:%02X:%02X:%02X:%02X. Send size=%d bytes, Receive size=%d bytes with queue depth=%d.\n", peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5],sendSize,receiveSize,queueDepth); init(); // initialize SpanPoint @@ -49,7 +56,8 @@ SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int memcpy(peerInfo.lmk, lmk, 16); // set local key esp_now_add_peer(&peerInfo); // add peer to ESP-NOW - receiveQueue = xQueueCreate(queueDepth,receiveSize); + if(receiveSize>0) + receiveQueue = xQueueCreate(queueDepth,receiveSize); SpanPoints.push_back(this); } @@ -71,25 +79,84 @@ void SpanPoint::init(const char *password){ memcpy(lmk, hash, 16); // store first 16 bytes of hash for later use as local key esp_now_set_pmk(hash+16); // set hash for primary key using last 16 bytes of hash esp_now_register_recv_cb(dataReceived); // set callback for receiving data + esp_now_register_send_cb(dataSent); // set callback for sending data + + statusQueue = xQueueCreate(1,sizeof(esp_now_send_status_t)); // create statusQueue even if not needed + setChannelMask(0x3FFE); // default channel mask uses channels 1-13 initialized=true; } /////////////////////////////// +void SpanPoint::setChannelMask(uint16_t mask){ + channelMask = mask & 0x3FFE; + + channel=0; + + for(int i=1;i<=13 && channel==0;i++) + channel=(channelMask & (1< channel=%d\n",channelMask,channel); +} + +/////////////////////////////// + boolean SpanPoint::get(void *dataBuf){ + if(receiveSize==0) + return(false); + return(xQueueReceive(receiveQueue, dataBuf, 0)); } /////////////////////////////// +boolean SpanPoint::send(void *data){ + + if(sendSize==0) + return(false); + + esp_now_send_status_t status = ESP_NOW_SEND_FAIL; + + for(int c=0;c<13;c++){ + if((1<0){ Serial.printf("\nFATAL ERROR! SpanPoint objects created in main hub device must be instantiated AFTER calling homeSpan.begin() ***\n"); Serial.printf("\n=== PROGRAM HALTED ==="); while(1); } + isHub=true; } @@ -103,6 +170,9 @@ void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, in if(it==SpanPoints.end()) return; + if((*it)->receiveSize==0) + return; + if(len!=(*it)->receiveSize){ Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->receiveSize); return; @@ -117,3 +187,6 @@ uint8_t SpanPoint::lmk[16]; boolean SpanPoint::initialized=false; boolean SpanPoint::isHub=false; vector SpanPoint::SpanPoints; +int SpanPoint::channel; +uint16_t SpanPoint::channelMask; +QueueHandle_t SpanPoint::statusQueue; diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index e455be5..aef6360 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -60,8 +60,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa SpanPoint::setAsHub(); - if(WiFi.getMode()!=WIFI_AP_STA) - WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) + WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) statusLED=new Blinker(statusDevice,autoOffLED); // create Status LED, even is statusDevice is NULL diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 6f8f7fc..7a0080b 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -765,16 +765,25 @@ class SpanPoint { static boolean initialized; static boolean isHub; static vector SpanPoints; + static int channel; // WiFi channel (1-13) + static uint16_t channelMask; // channel mask + static QueueHandle_t statusQueue; // queue for communication between SpanPoint::dataSend and SpanPoint::send static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); static void init(const char *password="HomeSpan"); static void setAsHub(); - + + static void dataSent(const uint8_t *mac, esp_now_send_status_t status) { + xQueueOverwrite( statusQueue, &status ); + } + public: SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth=1); static void setPassword(const char *pwd){init(pwd);}; + static void setChannelMask(uint16_t mask); boolean get(void *dataBuf); + boolean send(void *data); }; ///////////////////////////////////////////////// diff --git a/src/src.ino b/src/src.ino index f99ed28..566600a 100644 --- a/src/src.ino +++ b/src/src.ino @@ -50,12 +50,20 @@ void setup() { // homeSpan.enableAutoStartAP(); // homeSpan.setApFunction(myWiFiAP); + homeSpan.enableWebLog(10,"pool.ntp.org","UTC","myLog"); // creates a web log on the URL /HomeSpan-[DEVICE-ID].local:[TCP-PORT]/myLog + + homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); SpanPoint::setPassword("Hello Thert"); - dev1=new SpanPoint("AC:67:B2:77:42:20",0,sizeof(message_t)); + dev1=new SpanPoint("AC:67:B2:77:42:20",4,0); dev2=new SpanPoint("7C:DF:A1:61:E4:A8",0,sizeof(message_t)); + SpanPoint::setChannelMask(0x3FFE); + dev2->setChannelMask(1<<1); + dev2->setChannelMask(1<<3 | 1<<8 | 1<<13); + dev2->setChannelMask(1<<13); + new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics From 23afdb37115f38a89aad4e61b865c96531b50c1f Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 1 Oct 2022 15:33:11 -0500 Subject: [PATCH 19/75] Completed all SpanPoint code Allows for bi-directional transmission between main HomeSpan device and one or more remote devices. To Do: Create and test with Temperature Sensor Example --- src/HomePeer.cpp | 71 ----------------- src/HomePeer.h | 25 ------ src/HomePoint.cpp | 192 ---------------------------------------------- src/HomeSpan.cpp | 188 ++++++++++++++++++++++++++++++++++++++++++++- src/HomeSpan.h | 4 +- src/src.ino | 22 ++++-- 6 files changed, 201 insertions(+), 301 deletions(-) delete mode 100644 src/HomePeer.cpp delete mode 100644 src/HomePeer.h delete mode 100644 src/HomePoint.cpp diff --git a/src/HomePeer.cpp b/src/HomePeer.cpp deleted file mode 100644 index b50f155..0000000 --- a/src/HomePeer.cpp +++ /dev/null @@ -1,71 +0,0 @@ - -#include "HomePeer.h" - -#include -#include -#include -#include - -void SpanPeer::start(const char *macAddress, const char *password){ - - if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ - Serial.printf("*** ERROR: Can't start HomeSpan NOW! Bad MAC Address '%s'\n\n",macAddress); - return; - } - - statusQueue = xQueueCreate(1,sizeof(esp_now_send_status_t)); - - WiFi.mode(WIFI_AP_STA); - esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); - esp_now_init(); - esp_now_register_send_cb(onDataSent); - - uint8_t lmk[32]; - uint8_t mac[6]; - - mbedtls_sha256_ret((const unsigned char *)password,strlen(password),lmk,0); - esp_now_set_pmk(lmk+16); - - peerInfo.channel=0; - peerInfo.ifidx=WIFI_IF_STA; - peerInfo.encrypt = true; - memcpy(peerInfo.lmk, lmk, 16); - esp_now_add_peer(&peerInfo); - esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); - - Serial.printf("Started HomePeer: MAC Address = %s HomeSpan Address = %X:%X:%X:%X:%X:%X\n",WiFi.macAddress().c_str(), - peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5]); - started=true; -} - -boolean SpanPeer::send(uint8_t *data, size_t len){ - - if(!started){ - Serial.printf("*** ERROR: Can't send data until HomePeer has been started.\n\n"); - return(false); - } - - esp_now_send_status_t status = ESP_NOW_SEND_FAIL; - - for(int c=0;c<13;c++){ - if((1< -#include - -class SpanPeer { - - esp_now_peer_info_t peerInfo; - boolean started=false; - static QueueHandle_t statusQueue; - int channel=1; - uint16_t channelMask=0x3FFE; - - static void onDataSent(const uint8_t *mac, esp_now_send_status_t status) { - xQueueOverwrite( statusQueue, &status ); - } - - public: - - void setChannelMask(uint16_t cm){channelMask = cm & 0x3FFE;} - void start(const char *macAddress, const char *password="HomeSpan"); - boolean send(uint8_t *data, size_t len); - -}; - -extern SpanPeer homePeer; diff --git a/src/HomePoint.cpp b/src/HomePoint.cpp deleted file mode 100644 index 0650100..0000000 --- a/src/HomePoint.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/********************************************************************************* - * MIT License - * - * Copyright (c) 2020-2022 Gregg E. Berman - * - * https://github.com/HomeSpan/HomeSpan - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - ********************************************************************************/ - -#include "HomeSpan.h" -#include -#include - -SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth){ - - if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ - Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress); - Serial.printf("\n=== PROGRAM HALTED ==="); - while(1); - } - - if(sendSize<0 || sendSize>200 || receiveSize<0 || receiveSize>200 || queueDepth<1 || (sendSize==0 && receiveSize==0)){ - Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\",%d,%d,%d) - one or more invalid parameters ***\n",macAddress,sendSize,receiveSize,queueDepth); - Serial.printf("\n=== PROGRAM HALTED ==="); - while(1); - } - - this->sendSize=sendSize; - this->receiveSize=receiveSize; - - Serial.printf("SpanPoint: Created link to device with MAC Address %02X:%02X:%02X:%02X:%02X:%02X. Send size=%d bytes, Receive size=%d bytes with queue depth=%d.\n", - peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5],sendSize,receiveSize,queueDepth); - - init(); // initialize SpanPoint - peerInfo.channel=0; // 0 = matches current WiFi channel - peerInfo.ifidx=WIFI_IF_STA; // must specify interface - peerInfo.encrypt=true; // turn on encryption for this peer - memcpy(peerInfo.lmk, lmk, 16); // set local key - esp_now_add_peer(&peerInfo); // add peer to ESP-NOW - - if(receiveSize>0) - receiveQueue = xQueueCreate(queueDepth,receiveSize); - - SpanPoints.push_back(this); -} - -/////////////////////////////// - -void SpanPoint::init(const char *password){ - - if(initialized) - return; - - if(WiFi.getMode()!=WIFI_AP_STA) - WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) - - uint8_t hash[32]; - mbedtls_sha256_ret((const unsigned char *)password,strlen(password),hash,0); // produce 256-bit bit hash from password - - esp_now_init(); // initialize ESP-NOW - memcpy(lmk, hash, 16); // store first 16 bytes of hash for later use as local key - esp_now_set_pmk(hash+16); // set hash for primary key using last 16 bytes of hash - esp_now_register_recv_cb(dataReceived); // set callback for receiving data - esp_now_register_send_cb(dataSent); // set callback for sending data - - statusQueue = xQueueCreate(1,sizeof(esp_now_send_status_t)); // create statusQueue even if not needed - setChannelMask(0x3FFE); // default channel mask uses channels 1-13 - - initialized=true; -} - -/////////////////////////////// - -void SpanPoint::setChannelMask(uint16_t mask){ - channelMask = mask & 0x3FFE; - - channel=0; - - for(int i=1;i<=13 && channel==0;i++) - channel=(channelMask & (1< channel=%d\n",channelMask,channel); -} - -/////////////////////////////// - -boolean SpanPoint::get(void *dataBuf){ - - if(receiveSize==0) - return(false); - - return(xQueueReceive(receiveQueue, dataBuf, 0)); -} - -/////////////////////////////// - -boolean SpanPoint::send(void *data){ - - if(sendSize==0) - return(false); - - esp_now_send_status_t status = ESP_NOW_SEND_FAIL; - - for(int c=0;c<13;c++){ - if((1<0){ - Serial.printf("\nFATAL ERROR! SpanPoint objects created in main hub device must be instantiated AFTER calling homeSpan.begin() ***\n"); - Serial.printf("\n=== PROGRAM HALTED ==="); - while(1); - } - - isHub=true; -} - -/////////////////////////////// - -void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len){ - - auto it=SpanPoints.begin(); - for(;it!=SpanPoints.end() && memcmp((*it)->peerInfo.peer_addr,mac,6)!=0; it++); - - if(it==SpanPoints.end()) - return; - - if((*it)->receiveSize==0) - return; - - if(len!=(*it)->receiveSize){ - Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->receiveSize); - return; - } - - xQueueSend((*it)->receiveQueue, incomingData, 0); // send to queue - do not wait if queue is full and instead fail immediately since we need to return from this function ASAP -} - -/////////////////////////////// - -uint8_t SpanPoint::lmk[16]; -boolean SpanPoint::initialized=false; -boolean SpanPoint::isHub=false; -vector SpanPoint::SpanPoints; -int SpanPoint::channel; -uint16_t SpanPoint::channelMask; -QueueHandle_t SpanPoint::statusQueue; diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index aef6360..af2f89b 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "HomeSpan.h" #include "HAP.h" @@ -2152,6 +2153,189 @@ void SpanOTA::error(ota_error_t err){ else if (err == OTA_END_ERROR) Serial.println("End Failed\n"); } +/////////////////////////////// + +int SpanOTA::otaPercent; +boolean SpanOTA::safeLoad; +boolean SpanOTA::enabled=false; +boolean SpanOTA::auth; + +/////////////////////////////// +// SpanPoint // +/////////////////////////////// + +SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth){ + + if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){ + Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress); + Serial.printf("\n=== PROGRAM HALTED ==="); + while(1); + } + + if(sendSize<0 || sendSize>200 || receiveSize<0 || receiveSize>200 || queueDepth<1 || (sendSize==0 && receiveSize==0)){ + Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\",%d,%d,%d) - one or more invalid parameters ***\n",macAddress,sendSize,receiveSize,queueDepth); + Serial.printf("\n=== PROGRAM HALTED ==="); + while(1); + } + + this->sendSize=sendSize; + this->receiveSize=receiveSize; + + Serial.printf("SpanPoint: Created link to device with MAC Address %02X:%02X:%02X:%02X:%02X:%02X. Send size=%d bytes, Receive size=%d bytes with queue depth=%d.\n", + peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5],sendSize,receiveSize,queueDepth); + + init(); // initialize SpanPoint + peerInfo.channel=0; // 0 = matches current WiFi channel + peerInfo.ifidx=WIFI_IF_STA; // must specify interface + peerInfo.encrypt=true; // turn on encryption for this peer + memcpy(peerInfo.lmk, lmk, 16); // set local key + esp_now_add_peer(&peerInfo); // add peer to ESP-NOW + + if(receiveSize>0) + receiveQueue = xQueueCreate(queueDepth,receiveSize); + + SpanPoints.push_back(this); +} + +/////////////////////////////// + +void SpanPoint::init(const char *password){ + + if(initialized) + return; + + if(WiFi.getMode()!=WIFI_AP_STA) + WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) + + uint8_t hash[32]; + mbedtls_sha256_ret((const unsigned char *)password,strlen(password),hash,0); // produce 256-bit bit hash from password + + esp_now_init(); // initialize ESP-NOW + memcpy(lmk, hash, 16); // store first 16 bytes of hash for later use as local key + esp_now_set_pmk(hash+16); // set hash for primary key using last 16 bytes of hash + esp_now_register_recv_cb(dataReceived); // set callback for receiving data + esp_now_register_send_cb(dataSent); // set callback for sending data + + statusQueue = xQueueCreate(1,sizeof(esp_now_send_status_t)); // create statusQueue even if not needed + setChannelMask(channelMask); // default channel mask at start-up uses channels 1-13 + + initialized=true; +} + +/////////////////////////////// + +void SpanPoint::setChannelMask(uint16_t mask){ + channelMask = mask & 0x3FFE; + + if(isHub) + return; + + uint8_t channel=0; + + for(int i=1;i<=13 && channel==0;i++) // find first "allowed" channel based on mask + channel=(channelMask & (1<peerInfo.peer_addr,mac,6)!=0; it++); + + if(it==SpanPoints.end()) + return; + + if((*it)->receiveSize==0) + return; + + if(len!=(*it)->receiveSize){ + Serial.printf("SpanPoint Warning! %d bytes received from %02X:%02X:%02X:%02X:%02X:%02X does not match %d-byte queue size\n",len,mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],(*it)->receiveSize); + return; + } + + xQueueSend((*it)->receiveQueue, incomingData, 0); // send to queue - do not wait if queue is full and instead fail immediately since we need to return from this function ASAP +} + +/////////////////////////////// + +uint8_t SpanPoint::lmk[16]; +boolean SpanPoint::initialized=false; +boolean SpanPoint::isHub=false; +vector SpanPoint::SpanPoints; +uint16_t SpanPoint::channelMask=0x3FFE; +QueueHandle_t SpanPoint::statusQueue; + /////////////////////////////// // MISC // /////////////////////////////// @@ -2161,10 +2345,6 @@ void __attribute__((weak)) loop(){ /////////////////////////////// -int SpanOTA::otaPercent; -boolean SpanOTA::safeLoad; -boolean SpanOTA::enabled=false; -boolean SpanOTA::auth; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 7a0080b..6fd59be 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -765,13 +765,13 @@ class SpanPoint { static boolean initialized; static boolean isHub; static vector SpanPoints; - static int channel; // WiFi channel (1-13) static uint16_t channelMask; // channel mask static QueueHandle_t statusQueue; // queue for communication between SpanPoint::dataSend and SpanPoint::send static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len); static void init(const char *password="HomeSpan"); - static void setAsHub(); + static void setAsHub(){isHub=true;} + static uint8_t nextChannel(); static void dataSent(const uint8_t *mac, esp_now_send_status_t status) { xQueueOverwrite( statusQueue, &status ); diff --git a/src/src.ino b/src/src.ino index 566600a..7f16cf8 100644 --- a/src/src.ino +++ b/src/src.ino @@ -52,17 +52,17 @@ void setup() { homeSpan.enableWebLog(10,"pool.ntp.org","UTC","myLog"); // creates a web log on the URL /HomeSpan-[DEVICE-ID].local:[TCP-PORT]/myLog + SpanPoint::setPassword("Hello Thert"); + + homeSpan.setLogLevel(1); + + dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(int),0); + dev2=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(int),sizeof(message_t)); homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); - SpanPoint::setPassword("Hello Thert"); - dev1=new SpanPoint("AC:67:B2:77:42:20",4,0); - dev2=new SpanPoint("7C:DF:A1:61:E4:A8",0,sizeof(message_t)); + SpanPoint::setChannelMask(1<<13); - SpanPoint::setChannelMask(0x3FFE); - dev2->setChannelMask(1<<1); - dev2->setChannelMask(1<<3 | 1<<8 | 1<<13); - dev2->setChannelMask(1<<13); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments @@ -115,6 +115,8 @@ void setup() { } // end of setup() +unsigned long alarmTime=0; + ////////////////////////////////////// void loop(){ @@ -125,6 +127,12 @@ void loop(){ if(dev2->get(&message)) Serial.printf("DEV2: '%s' %d %f %d\n",message.a,message.b,message.c,message.d); + if(millis()-alarmTime>5000){ + alarmTime=millis(); + boolean success = dev2->send(&alarmTime); + Serial.printf("Success = %d\n",success); + } + } // end of loop() ////////////////////////////////////// From ab21a0c96c91c704057cdf21421964fbda8f627a Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 2 Oct 2022 10:14:09 -0500 Subject: [PATCH 20/75] Created Other Examples - RemoteSensors Created MainDevice and RemoteDevice sketches --- .../RemoteSensors/MainDevice/MainDevice.ino | 121 ++++++++++++++++++ .../RemoteDevice/RemoteDevice.ino | 78 +++++++++++ src/HomeSpan.cpp | 1 + src/HomeSpan.h | 4 +- 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 Other Examples/RemoteSensors/MainDevice/MainDevice.ino create mode 100644 Other Examples/RemoteSensors/RemoteDevice/RemoteDevice.ino diff --git a/Other Examples/RemoteSensors/MainDevice/MainDevice.ino b/Other Examples/RemoteSensors/MainDevice/MainDevice.ino new file mode 100644 index 0000000..270f4e4 --- /dev/null +++ b/Other Examples/RemoteSensors/MainDevice/MainDevice.ino @@ -0,0 +1,121 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////////////////////////////// +// // +// HomeSpan: A HomeKit implementation for the ESP32 // +// ------------------------------------------------ // +// // +// Demonstrates how to use SpanPoint() to implement // +// two remote temperature sensors on separate ESP32 // +// devices. // +// // +// This sketch is for the MAIN DEVICE that contains // +// all the usual HomeSpan logic, plus two instances // +// of SpanPoint to read temperatures from two other // +// remote devices. // +// // +//////////////////////////////////////////////////////////// + +#include "HomeSpan.h" + +struct RemoteTempSensor : Service::TemperatureSensor { + + SpanCharacteristic *temp; + SpanCharacteristic *fault; + SpanPoint *remoteTemp; + const char *name; + float temperature; + + RemoteTempSensor(const char *name, const char*macAddress) : Service::TemperatureSensor(){ + + this->name=name; + + temp=new Characteristic::CurrentTemperature(-10.0); // set initial temperature + temp->setRange(-50,100); // expand temperature range to allow negative values + + fault=new Characteristic::StatusFault(1); // set initial state = fault + + remoteTemp=new SpanPoint(macAddress,0,sizeof(float)); // create a SpanPoint with send size=0 and receive size=sizeof(float) + + } // end constructor + + void loop(){ + + if(remoteTemp->get(&temperature)){ // if there is data from the remote sensor + temp->setVal(temperature); // update temperature + fault->setVal(0); // clear fault + + LOG1("Sensor %s update: Temperature=%0.2f\n",name,temperature*9/5+32); + + } else if(remoteTemp->time()>60000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault + fault->setVal(1); // set fault state + LOG1("Sensor %s update: FAULT\n",name); + } + + } // loop + +}; + +////////////////////////////////////// + +void setup() { + + Serial.begin(115200); + + homeSpan.setLogLevel(1); + + homeSpan.begin(Category::Bridges,"Sensor Hub"); + + new SpanAccessory(); + new Service::AccessoryInformation(); + new Characteristic::Identify(); + + new SpanAccessory(); + new Service::AccessoryInformation(); + new Characteristic::Identify(); + new Characteristic::Name("Indoor Temp"); + new RemoteTempSensor("Device 1","AC:67:B2:77:42:20"); // pass MAC Address of Remote Device + + new SpanAccessory(); + new Service::AccessoryInformation(); + new Characteristic::Identify(); + new Characteristic::Name("Outdoor Temp"); + new RemoteTempSensor("Device 2","84:CC:A8:11:B4:84"); // pass MAC Address of Remote Device + + +} // end of setup() + +////////////////////////////////////// + +void loop(){ + + homeSpan.poll(); + +} // end of loop() + +////////////////////////////////////// diff --git a/Other Examples/RemoteSensors/RemoteDevice/RemoteDevice.ino b/Other Examples/RemoteSensors/RemoteDevice/RemoteDevice.ino new file mode 100644 index 0000000..04b9dca --- /dev/null +++ b/Other Examples/RemoteSensors/RemoteDevice/RemoteDevice.ino @@ -0,0 +1,78 @@ + +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////////////////////////////// +// // +// HomeSpan: A HomeKit implementation for the ESP32 // +// ------------------------------------------------ // +// // +// Demonstrates how to use SpanPoint() to implement // +// two remote temperature sensors on separate ESP32 // +// devices. // +// // +// This sketch is for the REMOTE DEVICES. They are // +// very simple and don't need any of the normal // +// HomeSpan logic (except for SpanPoint). // +// // +// Note this sketch only SIMULATES a temperature // +// sensor by slowly setting the temperature from // +// -30.0 to 35.0 C in steps of 0.5 C. The sketch // +// does not contain logic for an actual physical // +// temperature sensor. // +// // +//////////////////////////////////////////////////////////// + +#include "HomeSpan.h" + +float temperature=-10.0; +SpanPoint *mainDevice; + +void setup() { + + Serial.begin(115200); + delay(1000); + + Serial.printf("Starting\n\n"); + + // In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE + + mainDevice=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0 + + homeSpan.setLogLevel(1); +} + +void loop() { + + boolean success = mainDevice->send(&temperature); // this will show as success as long as the MAIN DEVICE is running + Serial.printf("Send %s\n",success?"Succeded":"Failed"); + temperature+=0.5; + if(temperature>35.0) + temperature=-30.0; + + delay(20000); +} diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index af2f89b..56660db 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -2324,6 +2324,7 @@ void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, in return; } + (*it)->receiveTime=millis(); // set time of receive xQueueSend((*it)->receiveQueue, incomingData, 0); // send to queue - do not wait if queue is full and instead fail immediately since we need to return from this function ASAP } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 6fd59be..3312d42 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -760,6 +760,7 @@ class SpanPoint { int sendSize; // size (in bytes) of messages to send esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data QueueHandle_t receiveQueue; // queue to store data after it is received + uint32_t receiveTime=0; // time (in millis) of most recent data received static uint8_t lmk[16]; static boolean initialized; @@ -783,7 +784,8 @@ class SpanPoint { static void setPassword(const char *pwd){init(pwd);}; static void setChannelMask(uint16_t mask); boolean get(void *dataBuf); - boolean send(void *data); + boolean send(void *data); + uint32_t time(){return(millis()-receiveTime);} }; ///////////////////////////////////////////////// From 0d5d4a0ca0769b6d0e042f5e5bf07fef58d0907b Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 2 Oct 2022 16:41:08 -0500 Subject: [PATCH 21/75] Added RemoteTempSensor to RemoteSensor Example --- .../RemoteSensors/MainDevice/MainDevice.ino | 2 +- .../RemoteTempSensor/RemoteTempSensor.ino | 108 ++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino diff --git a/Other Examples/RemoteSensors/MainDevice/MainDevice.ino b/Other Examples/RemoteSensors/MainDevice/MainDevice.ino index 270f4e4..3783ea2 100644 --- a/Other Examples/RemoteSensors/MainDevice/MainDevice.ino +++ b/Other Examples/RemoteSensors/MainDevice/MainDevice.ino @@ -72,7 +72,7 @@ struct RemoteTempSensor : Service::TemperatureSensor { LOG1("Sensor %s update: Temperature=%0.2f\n",name,temperature*9/5+32); - } else if(remoteTemp->time()>60000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault + } else if(remoteTemp->time()>120000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault fault->setVal(1); // set fault state LOG1("Sensor %s update: FAULT\n",name); } diff --git a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino new file mode 100644 index 0000000..65af908 --- /dev/null +++ b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino @@ -0,0 +1,108 @@ + +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////////////////////////////// +// // +// HomeSpan: A HomeKit implementation for the ESP32 // +// ------------------------------------------------ // +// // +// This sketch is for a Remote Temperature Sensor to // +// be used in conjunction with the "MainDevice.ino" // +// sketch running on a separate ESP32 // +// // +// The purpose of these sketches is to demonstrate // +// how to use SpanPoint() to communication between // +// a remote ESP32 device that takes measurements from // +// a sensor, and a separate "main" ESP32 device that // +// is running the full HomeSpan code, and thus // +// connects to HomeKit. // +// // +// This sketch implements an Adafruit ADT7410 I2C // +// Temperature Sensor. If you don't have such a // +// device, please use the sketch "RemoteDevice.ino" // +// instead. That sketch SIMULATES a temperature // +// sensor and therefore allows you to learn how // +// SpanPoint() works even though the temperature data // +// itself isn't real. // +// // +//////////////////////////////////////////////////////////// + +#include "HomeSpan.h" +#include // include the I2C library + +#define I2C_ADD 0x48 // ICS Address to use for the Adafruit ADT7410 + +SpanPoint *mainDevice; +uint32_t timer=0; // keep track of time since last update + +void setup() { + + Serial.begin(115200); + delay(1000); + + Serial.printf("Starting\n\n"); + + // In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE + + homeSpan.setLogLevel(1); + mainDevice=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0 + + Wire.begin(); // start I2C in Controller Mode + + Wire.beginTransmission(I2C_ADD); // setup transmission + Wire.write(0x0B); // ADT7410 Identification Register + Wire.endTransmission(0); // transmit and leave in restart mode to allow reading + Wire.requestFrom(I2C_ADD,1); // request read of single byte + uint8_t id = Wire.read(); // receive a byte + + Wire.beginTransmission(I2C_ADD); // setup transmission + Wire.write(0x03); // ADT740 Configuration Register + Wire.write(0xC0); // set 16-bit temperature resolution, 1 sampler per second + Wire.endTransmission(); // transmit + + LOG0("Configuring Temperature Sensor ADT7410 version 0x%02X with address 0x%02X.\n",id,I2C_ADD); // initialization message +} + +void loop() { + + if(millis()-timer>60000){ // only sample every 5 seconds + timer=millis(); + + Wire.beginTransmission(I2C_ADD); // setup transmission + Wire.write(0x00); // ADT7410 2-byte Temperature + Wire.endTransmission(0); // transmit and leave in restart mode to allow reading + Wire.requestFrom(I2C_ADD,2); // request read of two bytes + + int16_t iTemp = ((int16_t)Wire.read()<<8)+Wire.read(); + float temperature = iTemp/128.0; + + boolean success = mainDevice->send(&temperature); // send temperature to main device + LOG1("Send temp update of %0.2f C: %s\n",temperature,success?"Succeded":"Failed"); + } + } + From 2d411bab82aedc382f4655ca52d696d75148d311 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 3 Oct 2022 22:04:51 -0500 Subject: [PATCH 22/75] Updated SpanPoint so that WiFi_AP_STA is only used if receiveSize>0 Next up: explore power-reducing modes and sleep modes for remote sensors to extend battery life. --- .../RemoteSensors/MainDevice/MainDevice.ino | 2 +- .../RemoteTempSensor/RemoteTempSensor.ino | 4 +-- src/HomeSpan.cpp | 11 +++++--- src/src.ino | 27 ++++++++++--------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Other Examples/RemoteSensors/MainDevice/MainDevice.ino b/Other Examples/RemoteSensors/MainDevice/MainDevice.ino index 3783ea2..270f4e4 100644 --- a/Other Examples/RemoteSensors/MainDevice/MainDevice.ino +++ b/Other Examples/RemoteSensors/MainDevice/MainDevice.ino @@ -72,7 +72,7 @@ struct RemoteTempSensor : Service::TemperatureSensor { LOG1("Sensor %s update: Temperature=%0.2f\n",name,temperature*9/5+32); - } else if(remoteTemp->time()>120000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault + } else if(remoteTemp->time()>60000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault fault->setVal(1); // set fault state LOG1("Sensor %s update: FAULT\n",name); } diff --git a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino index 65af908..9e67cf4 100644 --- a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino +++ b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino @@ -58,7 +58,7 @@ #define I2C_ADD 0x48 // ICS Address to use for the Adafruit ADT7410 SpanPoint *mainDevice; -uint32_t timer=0; // keep track of time since last update +uint32_t timer=-30000; // keep track of time since last update void setup() { @@ -90,7 +90,7 @@ void setup() { void loop() { - if(millis()-timer>60000){ // only sample every 5 seconds + if(millis()-timer>30000){ // only sample every 30 seconds timer=millis(); Wire.beginTransmission(I2C_ADD); // setup transmission diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 56660db..003601e 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -60,8 +60,6 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa sprintf(this->category,"%d",(int)catID); SpanPoint::setAsHub(); - - WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) statusLED=new Blinker(statusDevice,autoOffLED); // create Status LED, even is statusDevice is NULL @@ -2183,6 +2181,11 @@ SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int Serial.printf("SpanPoint: Created link to device with MAC Address %02X:%02X:%02X:%02X:%02X:%02X. Send size=%d bytes, Receive size=%d bytes with queue depth=%d.\n", peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5],sendSize,receiveSize,queueDepth); + + if(receiveSize>0) + WiFi.mode(WIFI_AP_STA); + else if(WiFi.getMode()==WIFI_OFF) + WiFi.mode(WIFI_STA); init(); // initialize SpanPoint peerInfo.channel=0; // 0 = matches current WiFi channel @@ -2204,8 +2207,8 @@ void SpanPoint::init(const char *password){ if(initialized) return; - if(WiFi.getMode()!=WIFI_AP_STA) - WiFi.mode(WIFI_AP_STA); // set mode to mixed AP/STA. This does not start any servers, just configures the WiFi radio to ensure it does not sleep (required for ESP-NOW) + if(WiFi.getMode()==WIFI_OFF) + WiFi.mode(WIFI_STA); uint8_t hash[32]; mbedtls_sha256_ret((const unsigned char *)password,strlen(password),hash,0); // produce 256-bit bit hash from password diff --git a/src/src.ino b/src/src.ino index 7f16cf8..b66e78d 100644 --- a/src/src.ino +++ b/src/src.ino @@ -52,16 +52,17 @@ void setup() { homeSpan.enableWebLog(10,"pool.ntp.org","UTC","myLog"); // creates a web log on the URL /HomeSpan-[DEVICE-ID].local:[TCP-PORT]/myLog + SpanPoint::setChannelMask(1<<13); + SpanPoint::setPassword("Hello Thert"); homeSpan.setLogLevel(1); - dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(int),0); dev2=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(int),sizeof(message_t)); + dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(int),0); homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); - SpanPoint::setChannelMask(1<<13); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments @@ -122,16 +123,16 @@ unsigned long alarmTime=0; void loop(){ homeSpan.poll(); - if(dev1->get(&message)) - Serial.printf("DEV1: '%s' %d %f %d\n",message.a,message.b,message.c,message.d); - if(dev2->get(&message)) - Serial.printf("DEV2: '%s' %d %f %d\n",message.a,message.b,message.c,message.d); - - if(millis()-alarmTime>5000){ - alarmTime=millis(); - boolean success = dev2->send(&alarmTime); - Serial.printf("Success = %d\n",success); - } +// if(dev1->get(&message)) +// Serial.printf("DEV1: '%s' %d %f %d\n",message.a,message.b,message.c,message.d); +// if(dev2->get(&message)) +// Serial.printf("DEV2: '%s' %d %f %d\n",message.a,message.b,message.c,message.d); +// +// if(millis()-alarmTime>5000){ +// alarmTime=millis(); +// boolean success = dev2->send(&alarmTime); +// Serial.printf("Success = %d\n",success); +// } } // end of loop() @@ -146,6 +147,8 @@ void myWiFiAP(){ void wifiEstablished(){ Serial.print("IN CALLBACK FUNCTION\n\n"); + Serial.printf("MODE = %d\n",WiFi.getMode()); + } ////////////////////////////////////// From fffa3eb2ee3cd162a9bcd98856c270648a6e83ff Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 8 Oct 2022 18:16:44 -0500 Subject: [PATCH 23/75] Update RemoteTempSensor.ino Added power-saving and deep sleep --- .../RemoteTempSensor/RemoteTempSensor.ino | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino index 9e67cf4..49f8f8e 100644 --- a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino +++ b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino @@ -55,54 +55,54 @@ #include "HomeSpan.h" #include // include the I2C library -#define I2C_ADD 0x48 // ICS Address to use for the Adafruit ADT7410 +// #define DIAGNOSTIC_MODE + +#define SAMPLE_TIME 5000 // Time between temperature samples (in milliseconds) +#define I2C_ADD 0x48 // ICS Address to use for the Adafruit ADT7410 SpanPoint *mainDevice; -uint32_t timer=-30000; // keep track of time since last update void setup() { - + + setCpuFrequencyMhz(80); // reduce CPU frequency to save battery power + +#if defined(DIAGNOSTIC_MODE) + homeSpan.setLogLevel(1); Serial.begin(115200); delay(1000); - - Serial.printf("Starting\n\n"); +#endif // In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE - homeSpan.setLogLevel(1); mainDevice=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0 Wire.begin(); // start I2C in Controller Mode +#if defined(DIAGNOSTIC_MODE) Wire.beginTransmission(I2C_ADD); // setup transmission Wire.write(0x0B); // ADT7410 Identification Register Wire.endTransmission(0); // transmit and leave in restart mode to allow reading Wire.requestFrom(I2C_ADD,1); // request read of single byte uint8_t id = Wire.read(); // receive a byte + LOG1("Configuring Temperature Sensor ADT7410 version 0x%02X with address 0x%02X.\n",id,I2C_ADD); // initialization message +#endif Wire.beginTransmission(I2C_ADD); // setup transmission Wire.write(0x03); // ADT740 Configuration Register - Wire.write(0xC0); // set 16-bit temperature resolution, 1 sampler per second + Wire.write(0xC0); // set 16-bit temperature resolution, 1 sample per second Wire.endTransmission(); // transmit - - LOG0("Configuring Temperature Sensor ADT7410 version 0x%02X with address 0x%02X.\n",id,I2C_ADD); // initialization message -} + + Wire.beginTransmission(I2C_ADD); // setup transmission + Wire.write(0x00); // ADT7410 2-byte Temperature + Wire.endTransmission(0); // transmit and leave in restart mode to allow reading + Wire.requestFrom(I2C_ADD,2); // request read of two bytes -void loop() { - - if(millis()-timer>30000){ // only sample every 30 seconds - timer=millis(); - - Wire.beginTransmission(I2C_ADD); // setup transmission - Wire.write(0x00); // ADT7410 2-byte Temperature - Wire.endTransmission(0); // transmit and leave in restart mode to allow reading - Wire.requestFrom(I2C_ADD,2); // request read of two bytes + int16_t iTemp = ((int16_t)Wire.read()<<8)+Wire.read(); + float temperature = iTemp/128.0; - int16_t iTemp = ((int16_t)Wire.read()<<8)+Wire.read(); - float temperature = iTemp/128.0; - - boolean success = mainDevice->send(&temperature); // send temperature to main device - LOG1("Send temp update of %0.2f C: %s\n",temperature,success?"Succeded":"Failed"); - } - } + boolean success = mainDevice->send(&temperature); // send temperature to main device + + LOG1("Send temp update of %0.2f F: %s\n",temperature*9/5+32,success?"Succeded":"Failed"); + esp_deep_sleep(SAMPLE_TIME*1000); // enter deep sleep mode -- reboot after resuming +} From 8b3c09c3f4a01a9879015b5bba30d7a38f4461ab Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 8 Oct 2022 22:26:06 -0500 Subject: [PATCH 24/75] Update RemoteTempSensor.ino --- .../RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino index 49f8f8e..9aa70d0 100644 --- a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino +++ b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino @@ -57,7 +57,7 @@ // #define DIAGNOSTIC_MODE -#define SAMPLE_TIME 5000 // Time between temperature samples (in milliseconds) +#define SAMPLE_TIME 30000 // Time between temperature samples (in milliseconds) #define I2C_ADD 0x48 // ICS Address to use for the Adafruit ADT7410 SpanPoint *mainDevice; From f8bf0e00ef1cb76c24948e00f7ddc4c285dcebea Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 9 Oct 2022 07:24:45 -0500 Subject: [PATCH 25/75] Create NOW.md --- docs/NOW.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/NOW.md diff --git a/docs/NOW.md b/docs/NOW.md new file mode 100644 index 0000000..fdb238c --- /dev/null +++ b/docs/NOW.md @@ -0,0 +1,76 @@ +# Pulse Width Modulation (PWM) + +The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: + +`#include "extras/PwmPin.h"` + +## *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* + +Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output + * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) + * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 + + The following methods are supported: + +* `void set(float level)` + + * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) + +* `int getPin()` + + * returns the pin number (or -1 if LedPin was not successfully initialized) + +LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). + +* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` + + * *h* - input Hue value, range 0-360 + * *s* - input Saturation value, range 0-1 + * *v* - input Brightness value, range 0-1 + * *r* - output Red value, range 0-1 + * *g* - output Green value, range 0-1 + * *b* - output Blue value, range 0-1 + +See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. + +## *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* + +Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin + * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0°) + * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) + * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) + * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90°) + * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90°) + +The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: + +* `void set(double position)` + + * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. + +* `int getPin()` + + * returns the pin number (or -1 if ServoPin was not successfully initialized) + +A worked example showing how ServoPin can be used to control the Horizontal Tilt of a motorized Window Shade can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → ServoControl*](../Other%20Examples/ServoControl). + +### PWM Resource Allocation and Limitations + +The following PWM resources are available: + +* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) +* ESP32-S2: 8 Channels / 4 Timers +* ESP32-C3: 6 Channels / 4 Timers +* ESP32-S3: 8 Channels / 4 Timers + +HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* + +HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. + +--- + +[↩️](README.md) Back to the Welcome page From ac47b35c23d591192f8b615c2e2b406efed893b2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 9 Oct 2022 08:06:29 -0500 Subject: [PATCH 26/75] Update NOW.md --- docs/NOW.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index fdb238c..8be47cc 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,8 +1,16 @@ -# Pulse Width Modulation (PWM) +# Multi-Device Point-to-Point Communication using ESP-NOW -The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: +Like most commercial HomeKit devices, HomeSpan requires a power-consuming always-on WiFi connection, which unfortunately means powering a HomeKit device with batteries is generally not possible. For most applications, this is not a problem since your HomeSpan device will be controlling a real-world light, fan, thermostat, etc., and will likely be plugged into a wall outlet. However, there are some real-world applications where wall outlets are not readily available and battery-power is essential, such as remote temperature sensors, door and window sensors, or standalone switches. -`#include "extras/PwmPin.h"` +To address this problem, HomeSpan includes an integrated implementation of Espressif's ESP-NOW protocol that allows for the point-to-point transmission of short messages between ESP32 devices requiring **very little power**. Importantly, ESP-NOW uses the ESP32's existing WiFi radio, so no new hardware is required. + +What all this means is you can readily create a multi-device Accessory where: + +* HomeSpan, running on an ESP32 plugged into wall-power, provides the required always-on connectivity to HomeKit via your home WiFi network, and +* One or more remote devices, running on battery-powered ESP32's, monitor their local environment (e.g. temperature, humidity) and communicate this information back to the main HomeSpan device via the low-powered ESP-NOW protocol. +* The main HomeSpan device processes this information and updates HomeKit and the Home App as needed, just as if were taking its own temperature and humidity measurements directly. + +All of the required logic needed to implement point-to-point communication between multiple ESP32 devices is embedded in HomeSpan's easy-to-use SpanPoint class, fully described below. ## *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* From 6b9a541bd3fd3585312181c8bcc3f35af9fe7563 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 9 Oct 2022 09:13:07 -0500 Subject: [PATCH 27/75] Update NOW.md --- docs/NOW.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 8be47cc..5b28741 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,6 +1,9 @@ -# Multi-Device Point-to-Point Communication using ESP-NOW +# SpanPoint: Point-to-Point Communication between ESP32 Devices -Like most commercial HomeKit devices, HomeSpan requires a power-consuming always-on WiFi connection, which unfortunately means powering a HomeKit device with batteries is generally not possible. For most applications, this is not a problem since your HomeSpan device will be controlling a real-world light, fan, thermostat, etc., and will likely be plugged into a wall outlet. However, there are some real-world applications where wall outlets are not readily available and battery-power is essential, such as remote temperature sensors, door and window sensors, or standalone switches. +SpanPoint is HomeSpan's integrated implementation of Espressif's ESP-NOW protocol that enables bi-directional communication from one ESP32 device directly to another without the need for a central WiFi network. In a typical setup, one ESP32 device runs a full HomeSpan sketcs (perhaps for a Thermostat Accessory) and connects to HomeKit in the usual fashion. + + +ike most commercial HomeKit devices, HomeSpan requires a power-consuming always-on WiFi connection, which unfortunately means powering a HomeKit device with batteries is generally not possible. For most applications, this is not a problem since your HomeSpan device will be controlling a real-world light, fan, thermostat, etc., and will likely be plugged into a wall outlet. However, there are some real-world applications where wall outlets are not readily available and battery-power is essential, such as remote temperature sensors, door and window sensors, or standalone switches. To address this problem, HomeSpan includes an integrated implementation of Espressif's ESP-NOW protocol that allows for the point-to-point transmission of short messages between ESP32 devices requiring **very little power**. Importantly, ESP-NOW uses the ESP32's existing WiFi radio, so no new hardware is required. From a7914d442b5408f778e733f8a8d73eb7b1cb8d45 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 9 Oct 2022 15:49:14 -0500 Subject: [PATCH 28/75] Update NOW.md --- docs/NOW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/NOW.md b/docs/NOW.md index 5b28741..14eb8c6 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,6 +1,6 @@ # SpanPoint: Point-to-Point Communication between ESP32 Devices -SpanPoint is HomeSpan's integrated implementation of Espressif's ESP-NOW protocol that enables bi-directional communication from one ESP32 device directly to another without the need for a central WiFi network. In a typical setup, one ESP32 device runs a full HomeSpan sketcs (perhaps for a Thermostat Accessory) and connects to HomeKit in the usual fashion. +SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protocol. ESP-NOW provides bi-directional, point-to-point communication from one ESP32 device directly to another without the need for a central WiFi network. In a typical setup, a complete HomeSpan sketch runs "main" ESP32 device and connects to HomeKit by pairing with the Home App in the usual fashion. THen, one or more ike most commercial HomeKit devices, HomeSpan requires a power-consuming always-on WiFi connection, which unfortunately means powering a HomeKit device with batteries is generally not possible. For most applications, this is not a problem since your HomeSpan device will be controlling a real-world light, fan, thermostat, etc., and will likely be plugged into a wall outlet. However, there are some real-world applications where wall outlets are not readily available and battery-power is essential, such as remote temperature sensors, door and window sensors, or standalone switches. From 0625241f219a101315d1a26dbbe6ebccc493c264 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 9 Oct 2022 17:23:24 -0500 Subject: [PATCH 29/75] Update NOW.md --- docs/NOW.md | 51 +-------------------------------------------------- 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 14eb8c6..7453bad 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,19 +1,6 @@ # SpanPoint: Point-to-Point Communication between ESP32 Devices -SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protocol. ESP-NOW provides bi-directional, point-to-point communication from one ESP32 device directly to another without the need for a central WiFi network. In a typical setup, a complete HomeSpan sketch runs "main" ESP32 device and connects to HomeKit by pairing with the Home App in the usual fashion. THen, one or more - - -ike most commercial HomeKit devices, HomeSpan requires a power-consuming always-on WiFi connection, which unfortunately means powering a HomeKit device with batteries is generally not possible. For most applications, this is not a problem since your HomeSpan device will be controlling a real-world light, fan, thermostat, etc., and will likely be plugged into a wall outlet. However, there are some real-world applications where wall outlets are not readily available and battery-power is essential, such as remote temperature sensors, door and window sensors, or standalone switches. - -To address this problem, HomeSpan includes an integrated implementation of Espressif's ESP-NOW protocol that allows for the point-to-point transmission of short messages between ESP32 devices requiring **very little power**. Importantly, ESP-NOW uses the ESP32's existing WiFi radio, so no new hardware is required. - -What all this means is you can readily create a multi-device Accessory where: - -* HomeSpan, running on an ESP32 plugged into wall-power, provides the required always-on connectivity to HomeKit via your home WiFi network, and -* One or more remote devices, running on battery-powered ESP32's, monitor their local environment (e.g. temperature, humidity) and communicate this information back to the main HomeSpan device via the low-powered ESP-NOW protocol. -* The main HomeSpan device processes this information and updates HomeKit and the Home App as needed, just as if were taking its own temperature and humidity measurements directly. - -All of the required logic needed to implement point-to-point communication between multiple ESP32 devices is embedded in HomeSpan's easy-to-use SpanPoint class, fully described below. +SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protocol. ESP-NOW provides bi-directional, point-to-point communication between ESP32 devices without the need for a central WiFi network. In a typical setup, a "Main" ESP32 device runs a complete HomeSpan sketch whereas one or more "Remote" ESP32 devices run simple sketches designed to take measurements (temperature, humidity, etc.). The Main device is paired to HomeKit and communicates with the Home App over your WiFi network in the usual fashion. The Remote devices do not connect to your WiFi network or to HomeKit but instead send their data to the Main device using an encrypted ESP-NOW channel. The Main device is responsible for reading this data and determines what, if any, updates to send to HomeKit. ## *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* @@ -46,42 +33,6 @@ LedPin also includes a static class function that converts Hue/Saturation/Bright See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. -## *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* - -Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: - - * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin - * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0°) - * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) - * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) - * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90°) - * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90°) - -The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: - -* `void set(double position)` - - * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. - -* `int getPin()` - - * returns the pin number (or -1 if ServoPin was not successfully initialized) - -A worked example showing how ServoPin can be used to control the Horizontal Tilt of a motorized Window Shade can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → ServoControl*](../Other%20Examples/ServoControl). - -### PWM Resource Allocation and Limitations - -The following PWM resources are available: - -* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) -* ESP32-S2: 8 Channels / 4 Timers -* ESP32-C3: 6 Channels / 4 Timers -* ESP32-S3: 8 Channels / 4 Timers - -HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* - -HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. - --- [↩️](README.md) Back to the Welcome page From ba0496dd8e253a4d1986c821c6802db35d5975b5 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 9 Oct 2022 17:29:29 -0500 Subject: [PATCH 30/75] Update NOW.md --- docs/NOW.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/NOW.md b/docs/NOW.md index 7453bad..57fc089 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,6 +1,10 @@ # SpanPoint: Point-to-Point Communication between ESP32 Devices -SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protocol. ESP-NOW provides bi-directional, point-to-point communication between ESP32 devices without the need for a central WiFi network. In a typical setup, a "Main" ESP32 device runs a complete HomeSpan sketch whereas one or more "Remote" ESP32 devices run simple sketches designed to take measurements (temperature, humidity, etc.). The Main device is paired to HomeKit and communicates with the Home App over your WiFi network in the usual fashion. The Remote devices do not connect to your WiFi network or to HomeKit but instead send their data to the Main device using an encrypted ESP-NOW channel. The Main device is responsible for reading this data and determines what, if any, updates to send to HomeKit. +SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protocol. ESP-NOW provides bi-directional, point-to-point communication between ESP32 devices without the need for a central WiFi network. + +In a typical setup, a "Main" ESP32 device runs a complete HomeSpan sketch, whereas one or more "Remote" ESP32 devices run simple sketches designed to take measurements (temperature, humidity, etc.). The Main device is paired to HomeKit and communicates with the Home App over your WiFi network in the usual fashion. The Remote devices do not connect to your WiFi network or to HomeKit, but instead send their data directly to the Main device using an encrypted ESP-NOW channel. The Main device is then responsible for reading this data and determining what, if any, actions to take or updates to send to HomeKit. + +Most importantly, because ESP-NOW does not require always-on WiFi connectivity, it draws very little power. This means you can operate Remote devices with a battery instead of wall-power, which makes them much easier to place in remote locations (such as outdoors). ## *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* From 46b51411f0f19c93449827db33744b86709fcb82 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 9 Oct 2022 17:34:52 -0500 Subject: [PATCH 31/75] Update NOW.md --- docs/NOW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/NOW.md b/docs/NOW.md index 57fc089..48520c3 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -6,7 +6,7 @@ In a typical setup, a "Main" ESP32 device runs a complete HomeSpan sketch, where Most importantly, because ESP-NOW does not require always-on WiFi connectivity, it draws very little power. This means you can operate Remote devices with a battery instead of wall-power, which makes them much easier to place in remote locations (such as outdoors). -## *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* +## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: From 496e968775d61c6c1b5772d584f9230fb6c24dab Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 9 Oct 2022 18:07:14 -0500 Subject: [PATCH 32/75] Update NOW.md --- docs/NOW.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 48520c3..d0c50dd 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -8,9 +8,9 @@ Most importantly, because ESP-NOW does not require always-on WiFi connectivity, ## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* -Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** initializes an ESP-NOW connection from the current device to a device with the specified *macAddress*. Arguments, along with their defaults if left unspecified, are as follows: - * *pin* - the pin on which the PWM control signal will be output + * *macAddress* - the MAC Address of the device to which you are connecting, in the form "XX:XX:XX:XX:XX:XX" the pin on which the PWM control signal will be output * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 From e220ba4b34b661857f269a186ba861d6706c95a9 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 10 Oct 2022 06:44:34 -0500 Subject: [PATCH 33/75] Update NOW.md --- docs/NOW.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index d0c50dd..fd3eb9b 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -2,17 +2,19 @@ SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protocol. ESP-NOW provides bi-directional, point-to-point communication between ESP32 devices without the need for a central WiFi network. -In a typical setup, a "Main" ESP32 device runs a complete HomeSpan sketch, whereas one or more "Remote" ESP32 devices run simple sketches designed to take measurements (temperature, humidity, etc.). The Main device is paired to HomeKit and communicates with the Home App over your WiFi network in the usual fashion. The Remote devices do not connect to your WiFi network or to HomeKit, but instead send their data directly to the Main device using an encrypted ESP-NOW channel. The Main device is then responsible for reading this data and determining what, if any, actions to take or updates to send to HomeKit. +In a typical "Remote Sensor" setup, a "Main" ESP32 device runs a complete HomeSpan sketch, whereas one or more "Remote" ESP32 devices run simple sketches designed to take measurements (temperature, humidity, etc.). The Main device is paired to HomeKit and communicates with the Home App over your WiFi network in the usual fashion. In contrast, the Remote devices do not connect to your WiFi network or to HomeKit, but instead send their data directly to the Main device using an encrypted ESP-NOW channel. The Main device is then responsible for reading this data and determining what, if any, actions to take or updates to send to HomeKit. -Most importantly, because ESP-NOW does not require always-on WiFi connectivity, it draws very little power. This means you can operate Remote devices with a battery instead of wall-power, which makes them much easier to place in remote locations (such as outdoors). +You are not limited to the above scenario. SpanPoint can be used to communicate among devices that are each running a HomeSpan sketch, as well as among devices that are not running a HomeSpan sketch. As long as your sketch contains `#include "HomeSpan.h"`, you can use SpanPoint. + +Perhaps most importantly, because ESP-NOW does not require always-on WiFi connectivity, it draws very little power. This means you can operate Remote devices with a battery instead of wall-power, which opens up many possibilities, such as installing temperature sensors in remote (outdoor) locations where wall-power may be unavailable. ## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* Creating an instance of this **class** initializes an ESP-NOW connection from the current device to a device with the specified *macAddress*. Arguments, along with their defaults if left unspecified, are as follows: - * *macAddress* - the MAC Address of the device to which you are connecting, in the form "XX:XX:XX:XX:XX:XX" the pin on which the PWM control signal will be output - * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) - * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 + * *macAddress* - the MAC Address of the device to which you are connecting, in the standard 6-byte form "XX:XX:XX:XX:XX:XX", where XX represents a single hexidecimal byte + * *sendSize* - the size, in bytes, of any messages to be sent from this device over this instance. Set to zero if not sending messages from this device. Maximum allowed size if 200 bytes. + * *receiveSize* - the size, in bytes, of any messages to be received by this device over this instance. Set to zero if not receiving messages on this device. Maximum allowed size if 200 bytes. The following methods are supported: From 612394a0522e200f31686616206227464a557a6e Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 15 Oct 2022 10:20:21 -0500 Subject: [PATCH 34/75] Update RemoteTempSensor.ino --- .../RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino index 9aa70d0..4157707 100644 --- a/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino +++ b/Other Examples/RemoteSensors/RemoteTempSensor/RemoteTempSensor.ino @@ -55,7 +55,7 @@ #include "HomeSpan.h" #include // include the I2C library -// #define DIAGNOSTIC_MODE +#define DIAGNOSTIC_MODE #define SAMPLE_TIME 30000 // Time between temperature samples (in milliseconds) #define I2C_ADD 0x48 // ICS Address to use for the Adafruit ADT7410 @@ -70,6 +70,7 @@ void setup() { homeSpan.setLogLevel(1); Serial.begin(115200); delay(1000); + Serial.printf("Starting Remote Temperature Sensor. MAC Address of this device = %s\n",WiFi.macAddress().c_str()); #endif // In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE From 0325978a5b292221a582732f96727c6800656c1d Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 15 Oct 2022 16:47:08 -0500 Subject: [PATCH 35/75] Update NOW.md --- docs/NOW.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index fd3eb9b..38c3ac2 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -4,23 +4,76 @@ SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protoc In a typical "Remote Sensor" setup, a "Main" ESP32 device runs a complete HomeSpan sketch, whereas one or more "Remote" ESP32 devices run simple sketches designed to take measurements (temperature, humidity, etc.). The Main device is paired to HomeKit and communicates with the Home App over your WiFi network in the usual fashion. In contrast, the Remote devices do not connect to your WiFi network or to HomeKit, but instead send their data directly to the Main device using an encrypted ESP-NOW channel. The Main device is then responsible for reading this data and determining what, if any, actions to take or updates to send to HomeKit. -You are not limited to the above scenario. SpanPoint can be used to communicate among devices that are each running a HomeSpan sketch, as well as among devices that are not running a HomeSpan sketch. As long as your sketch contains `#include "HomeSpan.h"`, you can use SpanPoint. +Note that since ESP-NOW does not require always-on WiFi connectivity, it draws very little power. This means you can operate Remote devices with a battery instead of wall-power, which opens up many possibilities, such as installing temperature sensors in remote (outdoor) locations where wall-power may be unavailable. -Perhaps most importantly, because ESP-NOW does not require always-on WiFi connectivity, it draws very little power. This means you can operate Remote devices with a battery instead of wall-power, which opens up many possibilities, such as installing temperature sensors in remote (outdoor) locations where wall-power may be unavailable. +You are of course not limited to the above Main/Remote scenario. SpanPoint can be used to send messages back and forth among any combination of ESP32 devices, regardless if none or them, some of them, or all of them, are running a complete HomeSpan sketch. + +SpanPoint is part of the main HomeSpan library, so all you need to do is place `#include "HomeSpan.h"` near the top of your sketch to access the *SpanPoint* class and all of its methods. ## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* -Creating an instance of this **class** initializes an ESP-NOW connection from the current device to a device with the specified *macAddress*. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "matching" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows: - * *macAddress* - the MAC Address of the device to which you are connecting, in the standard 6-byte form "XX:XX:XX:XX:XX:XX", where XX represents a single hexidecimal byte - * *sendSize* - the size, in bytes, of any messages to be sent from this device over this instance. Set to zero if not sending messages from this device. Maximum allowed size if 200 bytes. - * *receiveSize* - the size, in bytes, of any messages to be received by this device over this instance. Set to zero if not receiving messages on this device. Maximum allowed size if 200 bytes. + * *macAddress* - the MAC Address of the other device to which you want to send data to, and/or receive data from, in the standard 6-byte format "XX:XX:XX:XX:XX:XX", where each XX represents a single 2-digit hexidecimal byte from 00 to FF + * *sendSize* - the size, in bytes, of any message that will be sent from this device to the device with MAC Address specified above. Maximum allowed size = 200 bytes. Set to zero if you will *not* be sending any messages from this device over this instance of *SpanPoint* + * *receiveSize* - the size, in bytes, of any message that will be received by this device from the device with MAC Address specified above. Maximum allowed size = 200 bytes. Set to zero if you do not intend to be reading any messages from the other device over this instance of *SpanPoint* (in which case any messages the other device happens to transmit to this device will be ignored) + * *queueDepth* - the depth of the queue reserved to hold incoming messages of *receiveSize* bytes. Default=1 is left unspecified, which should be sufficient for most applications. Set to greater than 1 if you expect to receive incoming messages from the other device at a rate that is faster than can be read and processed by the current device (a very unlikely scenario) - The following methods are supported: +For example, if: + + * **Device A** (MAC Address=*A1:A2:A3:A4:A5:A6*) is going to send 4-byte messages to both **Device B** (MAC Address=*B1:B2:B3:B4:B5:B6*) and **Device C** (MAC Addresses=*C1:C2:C3:C4:C5:C6*), and + * **Device B** is going to send 128-byte messages to **Device A**, and + * **Device C** is going to send 91-byte messages to **Device A**, + +then the sketch for **Device A** would contain something like this: + +```C++ +#include "HomeSpan.h" + +SpanPoint *devB, *devC; + +void setup(){ + devB = new SpanPoint("B1:B2:B3:B4:B5:B6",4,128); // create SpanPoint to device B with sendSize=4 and receiveSize=128 + devC = new SpanPoint("C1:C2:C3:C4:C5:C6",4,91); // create SpanPoint to device C with sendSize=4 and receiveSize=91 + ... +} +``` + +the sketch for **Device B** would contain something like this: + +```C++ +#include "HomeSpan.h" + +SpanPoint *devA; + +void setup(){ + devA = new SpanPoint("A1:A2:A3:A4:A5:A6",128,4); // create SpanPoint to device A with sendSize=128 and receiveSize=4 + ... +} +``` + +and the sketch for **Device C** would contain something like this: + +```C++ +#include "HomeSpan.h" + +SpanPoint *devA; + +void setup(){ + devA = new SpanPoint("A1:A2:A3:A4:A5:A6",91,4); // create SpanPoint to device A with sendSize=91 and receiveSize=4 + ... +} +``` + +Once instances of *SpanPoint* are created, the following methods can be used to send and receive messages: + +* `boolean send(void *data)` + + * transmits data referenced by the pointer *data* to the other device + * returns true if transmission was successful, otherwise false if transmission failed. Note that this method will return true as long as it can find and connect to the device with the *macAddress* specified when the relevant SpanPoint object was instantiated, regardless of whether or not the other device successful reads the message + * example (using the above scenario): `float humidity=35.8; devB->send(&humidity);` sends a 4-byte floating number to **Device B**. Note that the pointer *data* should always reference a data element or structure whose size exactly matches the *sendSize* specified when the relevant SpanPoint object was instantiated -* `void set(float level)` - * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) * `int getPin()` From 11d37a7b391c8e104f15ea9cc7a526f334585f52 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 15 Oct 2022 17:18:40 -0500 Subject: [PATCH 36/75] Update NOW.md --- docs/NOW.md | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 38c3ac2..d5814e2 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -70,26 +70,17 @@ Once instances of *SpanPoint* are created, the following methods can be used to * `boolean send(void *data)` * transmits data referenced by the pointer *data* to the other device - * returns true if transmission was successful, otherwise false if transmission failed. Note that this method will return true as long as it can find and connect to the device with the *macAddress* specified when the relevant SpanPoint object was instantiated, regardless of whether or not the other device successful reads the message - * example (using the above scenario): `float humidity=35.8; devB->send(&humidity);` sends a 4-byte floating number to **Device B**. Note that the pointer *data* should always reference a data element or structure whose size exactly matches the *sendSize* specified when the relevant SpanPoint object was instantiated - - + * returns **true** if transmission was successful, otherwise **false** if transmission failed. Note that this method will return **true** as long as it can find and connect to the device with the *macAddress* specified when the relevant SpanPoint object was instantiated, regardless of whether or not the other device successful reads the message + * example (using the above scenario): `float humidity=35.8; devB->send(&humidity);` sends a 4-byte floating number to **Device B**. Note that the pointer *data* should always reference a data element or structure whose size exactly matches the *sendSize* parameter specified when the relevant SpanPoint object was instantiated -* `int getPin()` - - * returns the pin number (or -1 if LedPin was not successfully initialized) - -LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). - -* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` - - * *h* - input Hue value, range 0-360 - * *s* - input Saturation value, range 0-1 - * *v* - input Brightness value, range 0-1 - * *r* - output Red value, range 0-1 - * *g* - output Green value, range 0-1 - * *b* - output Blue value, range 0-1 +* `boolean get(void *dataBuf)` + * checks to see if a message has been received into SpanPoint's internal queue from a device with the *macAddress* specified when the relevant SpanPoint object was instantiated. If so, the message is copied into the memory pointed to by *dataBuf*, the internal queue is cleared, and the method returns **true**. If no message is available, the *dataBuf* memory is unmodified and the method returns **false** + * note that the pointer *dataBuf* should always reference a data element or structure whose size equals or exceeds the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated + * once the internal SpanPoint queue is full, any messages received from the same device are discarded until the queue is cleared by calling this *get()* method. Setting the optional *queueDepth* parameter to a number greater than one when the relevant SpanPoint object was instantiated allows SpanPoint to retain up to *queueDepth* messages even without a call to the this *get()* method. However, once the queue is full, additional messages received will be discarded until messages are "gotten" of the queue with the *get()* method + + + See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. --- From ec11a56154047054822936a4a64e44fb36900cd7 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 15 Oct 2022 17:20:38 -0500 Subject: [PATCH 37/75] Update NOW.md --- docs/NOW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/NOW.md b/docs/NOW.md index d5814e2..76e2acb 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -77,7 +77,7 @@ Once instances of *SpanPoint* are created, the following methods can be used to * checks to see if a message has been received into SpanPoint's internal queue from a device with the *macAddress* specified when the relevant SpanPoint object was instantiated. If so, the message is copied into the memory pointed to by *dataBuf*, the internal queue is cleared, and the method returns **true**. If no message is available, the *dataBuf* memory is unmodified and the method returns **false** * note that the pointer *dataBuf* should always reference a data element or structure whose size equals or exceeds the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated - * once the internal SpanPoint queue is full, any messages received from the same device are discarded until the queue is cleared by calling this *get()* method. Setting the optional *queueDepth* parameter to a number greater than one when the relevant SpanPoint object was instantiated allows SpanPoint to retain up to *queueDepth* messages even without a call to the this *get()* method. However, once the queue is full, additional messages received will be discarded until messages are "gotten" of the queue with the *get()* method + * once the internal SpanPoint queue is full, any messages received from the same device are discarded until the queue is cleared by calling this *get()* method. Setting the optional *queueDepth* parameter to a number greater than one when the relevant SpanPoint object was instantiated allows SpanPoint to retain up to *queueDepth* messages even without a call to the *get()* method. However, once the queue is full, additional messages received will be discarded until messages are "gotten" off the queue with the *get()* method From 495a7f81ec4ee57a303d9806cb63486f126319a2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 09:51:04 -0500 Subject: [PATCH 38/75] Update NOW.md --- docs/NOW.md | 93 ++++++++++++++++------------------------------------- 1 file changed, 28 insertions(+), 65 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 76e2acb..480578c 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,85 +1,48 @@ # SpanPoint: Point-to-Point Communication between ESP32 Devices -SpanPoint is HomeSpan's easy-to-use implementation of Espressif's ESP-NOW protocol. ESP-NOW provides bi-directional, point-to-point communication between ESP32 devices without the need for a central WiFi network. +SpanPoint is HomeSpan's easy-to-use implementation of the Espressif ESP-NOW protocol. SpanPoint provides bi-directional, point-to-point communication of small, fixed-size messages directly between ESP32 devices based on their MAC Addresses without the need for a central WiFi network. -In a typical "Remote Sensor" setup, a "Main" ESP32 device runs a complete HomeSpan sketch, whereas one or more "Remote" ESP32 devices run simple sketches designed to take measurements (temperature, humidity, etc.). The Main device is paired to HomeKit and communicates with the Home App over your WiFi network in the usual fashion. In contrast, the Remote devices do not connect to your WiFi network or to HomeKit, but instead send their data directly to the Main device using an encrypted ESP-NOW channel. The Main device is then responsible for reading this data and determining what, if any, actions to take or updates to send to HomeKit. +To establish connectivity between any two devices, say **DEV-A** and **DEV-B**, simply create a SpanPoint object on each device that includes the MAC Address of the other device, the size of the messages (if any) to be sent from **DEV-A** to **DEV-B**, and the size of the messages (if any) to be sent from **DEV-B** to **DEV-A**. -Note that since ESP-NOW does not require always-on WiFi connectivity, it draws very little power. This means you can operate Remote devices with a battery instead of wall-power, which opens up many possibilities, such as installing temperature sensors in remote (outdoor) locations where wall-power may be unavailable. +Then, whenever needed, use the corresponding SpanPoint object's `send()` and `get()` methods to *send* a message from one device to the other, and *get* the message on the other device once it is received. -You are of course not limited to the above Main/Remote scenario. SpanPoint can be used to send messages back and forth among any combination of ESP32 devices, regardless if none or them, some of them, or all of them, are running a complete HomeSpan sketch. +SpanPoint takes takes care of all the underlying ESP-NOW structures and processes, configures ESP-NOW to encrypt all message traffic, manages internal data queues to make sure messages are not dropped, and automatically calibrates the WiFi channel used by ESP-NOW to match whatever is needed by any devices that are also connected to HomeKit through your central WiFi network. -SpanPoint is part of the main HomeSpan library, so all you need to do is place `#include "HomeSpan.h"` near the top of your sketch to access the *SpanPoint* class and all of its methods. +SpanPoint is part of the main HomeSpan library and is accessible by adding `#include "HomeSpan.h"` near the top of your sketch. Detailed descriptions of the SpanPoint class and all of its methods are provided below. ## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* -Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "matching" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "corresponding" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows: - * *macAddress* - the MAC Address of the other device to which you want to send data to, and/or receive data from, in the standard 6-byte format "XX:XX:XX:XX:XX:XX", where each XX represents a single 2-digit hexidecimal byte from 00 to FF - * *sendSize* - the size, in bytes, of any message that will be sent from this device to the device with MAC Address specified above. Maximum allowed size = 200 bytes. Set to zero if you will *not* be sending any messages from this device over this instance of *SpanPoint* - * *receiveSize* - the size, in bytes, of any message that will be received by this device from the device with MAC Address specified above. Maximum allowed size = 200 bytes. Set to zero if you do not intend to be reading any messages from the other device over this instance of *SpanPoint* (in which case any messages the other device happens to transmit to this device will be ignored) - * *queueDepth* - the depth of the queue reserved to hold incoming messages of *receiveSize* bytes. Default=1 is left unspecified, which should be sufficient for most applications. Set to greater than 1 if you expect to receive incoming messages from the other device at a rate that is faster than can be read and processed by the current device (a very unlikely scenario) - -For example, if: + * *macAddress* - the MAC Address of the *other* device to which you want to send data to, and/or receive data from, in the standard 6-byte format "XX:XX:XX:XX:XX:XX", where each XX represents a single 2-digit hexidecimal byte from 00 to FF + * *sendSize* - the size, in bytes, of any messages that will be sent from this device to the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `send()` to transmit any messages from this device to the *other* device + * *receiveSize* - the size, in bytes, of any messages that will be received by this device from the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `get()` to retreive any messages transmitted by the *other* device and received on this device + * *queueDepth* - the depth of the queue reserved to hold messages of *receiveSize* bytes that were received by this device from the *other* device, but not yet retreived using `get()`. See below for further details. Default=1 if left unspecified, which should be sufficient for most applications - * **Device A** (MAC Address=*A1:A2:A3:A4:A5:A6*) is going to send 4-byte messages to both **Device B** (MAC Address=*B1:B2:B3:B4:B5:B6*) and **Device C** (MAC Addresses=*C1:C2:C3:C4:C5:C6*), and - * **Device B** is going to send 128-byte messages to **Device A**, and - * **Device C** is going to send 91-byte messages to **Device A**, - -then the sketch for **Device A** would contain something like this: +SpanPoint objects created on two separate devices are considered "corresponding" if the MAC Addresses specified in each object reference each other, and the *sendSize* and *receiveSize* of one the SpanPoint object on one device matches the *receiveSize* and *sendSize* of the SpanPoint object on the "other" device, with the exception that it is always okay to set either the *sendSize* or *receiveSize* to zero regardless of the value set on the "other" device. -```C++ -#include "HomeSpan.h" +SpanPoint will throw a fatal error and halt the sketch if: + * the *macAddress* specified is ill-formed, or + * either *sendSize* or *receiveSize* is set to greater than 200, or + * both *sendSize* and *receiveSize* are set to 0, since there is no purpose for a SpanPoint that will neither transmit nor receive data + +The following SpanPoint methods are used to transmit and receive messages from a SpanPoint object on one device to a corresponding SpanPoint object on the "other" device: -SpanPoint *devB, *devC; +* `boolean send(const void *data)` -void setup(){ - devB = new SpanPoint("B1:B2:B3:B4:B5:B6",4,128); // create SpanPoint to device B with sendSize=4 and receiveSize=128 - devC = new SpanPoint("C1:C2:C3:C4:C5:C6",4,91); // create SpanPoint to device C with sendSize=4 and receiveSize=91 - ... -} -``` - -the sketch for **Device B** would contain something like this: - -```C++ -#include "HomeSpan.h" - -SpanPoint *devA; - -void setup(){ - devA = new SpanPoint("A1:A2:A3:A4:A5:A6",128,4); // create SpanPoint to device A with sendSize=128 and receiveSize=4 - ... -} -``` - -and the sketch for **Device C** would contain something like this: - -```C++ -#include "HomeSpan.h" - -SpanPoint *devA; - -void setup(){ - devA = new SpanPoint("A1:A2:A3:A4:A5:A6",91,4); // create SpanPoint to device A with sendSize=91 and receiveSize=4 - ... -} -``` - -Once instances of *SpanPoint* are created, the following methods can be used to send and receive messages: - -* `boolean send(void *data)` - - * transmits data referenced by the pointer *data* to the other device - * returns **true** if transmission was successful, otherwise **false** if transmission failed. Note that this method will return **true** as long as it can find and connect to the device with the *macAddress* specified when the relevant SpanPoint object was instantiated, regardless of whether or not the other device successful reads the message - * example (using the above scenario): `float humidity=35.8; devB->send(&humidity);` sends a 4-byte floating number to **Device B**. Note that the pointer *data* should always reference a data element or structure whose size exactly matches the *sendSize* parameter specified when the relevant SpanPoint object was instantiated + * transmits a message using data pointed to by *data* (which may be a standard data type, such as *uint16_t*, or a user-defined *struct*) to the *other* device + * the size of the *data* element to be transmitted much match the *sendSize* parameter specified when the SpanPoint object was created + * returns **true** if transmission was successful, otherwise **false** if transmission failed. Note that transmission is considered successful as long as the device can find and connect to the *other* device based on its MAC Address, regardless of whether or not the other device has a corresponding SpanPoint object * `boolean get(void *dataBuf)` - * checks to see if a message has been received into SpanPoint's internal queue from a device with the *macAddress* specified when the relevant SpanPoint object was instantiated. If so, the message is copied into the memory pointed to by *dataBuf*, the internal queue is cleared, and the method returns **true**. If no message is available, the *dataBuf* memory is unmodified and the method returns **false** - * note that the pointer *dataBuf* should always reference a data element or structure whose size equals or exceeds the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated - * once the internal SpanPoint queue is full, any messages received from the same device are discarded until the queue is cleared by calling this *get()* method. Setting the optional *queueDepth* parameter to a number greater than one when the relevant SpanPoint object was instantiated allows SpanPoint to retain up to *queueDepth* messages even without a call to the *get()* method. However, once the queue is full, additional messages received will be discarded until messages are "gotten" off the queue with the *get()* method - - + * checks to see if a message has been received into SpanPoint's internal message queue from the *other* device + * if no message is available, the method returns **false** and *dataBuf* is unmodified + * if a message is available, it is **moved** from the internal message queue into *dataBuf* and the method returns **true** + * the size of the message will always be equal to the *receiveSize* parameter specified when the SpanPoint object was created, so make sure *dataBuf* is sufficiently sized to store such a message + +Note that whether or or not you call the `get()` method, SpanPoint is configured to store in an internal queue any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated (i.e. each SpanPoint object maintains its own internal queue). If the internal queue is already full, the message will be ignored and thus never retrievable by the `get()` method. To avoid this, set the *queueDepth* to something greater than 1 when instantating the SpanPoint object. If the queue is not full, but the sie of the message + See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. From 1ace97984bfb45abe323b2b46183bb7dc4bdfbdf Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 10:01:52 -0500 Subject: [PATCH 39/75] Update NOW.md --- docs/NOW.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 480578c..0d3a55d 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -41,9 +41,10 @@ The following SpanPoint methods are used to transmit and receive messages from a * if a message is available, it is **moved** from the internal message queue into *dataBuf* and the method returns **true** * the size of the message will always be equal to the *receiveSize* parameter specified when the SpanPoint object was created, so make sure *dataBuf* is sufficiently sized to store such a message -Note that whether or or not you call the `get()` method, SpanPoint is configured to store in an internal queue any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated (i.e. each SpanPoint object maintains its own internal queue). If the internal queue is already full, the message will be ignored and thus never retrievable by the `get()` method. To avoid this, set the *queueDepth* to something greater than 1 when instantating the SpanPoint object. If the queue is not full, but the sie of the message +Note that whether or or not you call the `get()` method, SpanPoint is configured to store in an internal queue any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated. Each SpanPoint object maintains its own internal queue, which can be set tro hold more than one message by setting the *queueDepth* parameter to something greater than 1 when instantating the SpanPoint object. + +If the internal queue is full when a message is received, the message is not moved to the queue and will thus never be retrievable by the `get()` method. Regardless of whether or not the queue if full, if the size of a received messages does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded and if *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will be output on the Serial Monitor. - See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. --- From 774b9b45b3b0948c36cef94d7b13f16a2a279d44 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 10:03:56 -0500 Subject: [PATCH 40/75] Update NOW.md --- docs/NOW.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 0d3a55d..9e99471 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -41,9 +41,9 @@ The following SpanPoint methods are used to transmit and receive messages from a * if a message is available, it is **moved** from the internal message queue into *dataBuf* and the method returns **true** * the size of the message will always be equal to the *receiveSize* parameter specified when the SpanPoint object was created, so make sure *dataBuf* is sufficiently sized to store such a message -Note that whether or or not you call the `get()` method, SpanPoint is configured to store in an internal queue any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated. Each SpanPoint object maintains its own internal queue, which can be set tro hold more than one message by setting the *queueDepth* parameter to something greater than 1 when instantating the SpanPoint object. +Note that whether or or not you call the `get()` method, SpanPoint is configured to store in an internal queue any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated. Each SpanPoint object maintains its own internal queue, which can be set to hold more than one message by setting the *queueDepth* parameter to something greater than 1 when instantating the SpanPoint object. -If the internal queue is full when a message is received, the message is not moved to the queue and will thus never be retrievable by the `get()` method. Regardless of whether or not the queue if full, if the size of a received messages does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded and if *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will be output on the Serial Monitor. +If the internal queue is full when a message is received, the message is not moved to the queue and will thus never be retrievable by the `get()` method. Regardless of whether or not the queue if full, if the size of a received message does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded. If *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will also be output on the Serial Monitor. See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. From 707efa97eb547343762b3f0904a56f4641b74995 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 13:43:30 -0500 Subject: [PATCH 41/75] Update NOW.md --- docs/NOW.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index 9e99471..c89ac36 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -2,37 +2,35 @@ SpanPoint is HomeSpan's easy-to-use implementation of the Espressif ESP-NOW protocol. SpanPoint provides bi-directional, point-to-point communication of small, fixed-size messages directly between ESP32 devices based on their MAC Addresses without the need for a central WiFi network. -To establish connectivity between any two devices, say **DEV-A** and **DEV-B**, simply create a SpanPoint object on each device that includes the MAC Address of the other device, the size of the messages (if any) to be sent from **DEV-A** to **DEV-B**, and the size of the messages (if any) to be sent from **DEV-B** to **DEV-A**. +To establish connectivity between any two devices simply instantiate a SpanPoint object on each device that references the MAC Address of the other device, as well as specifies the (potentially different) sizes of the messages that each device is expected to send to, and receive from, the other. -Then, whenever needed, use the corresponding SpanPoint object's `send()` and `get()` methods to *send* a message from one device to the other, and *get* the message on the other device once it is received. - -SpanPoint takes takes care of all the underlying ESP-NOW structures and processes, configures ESP-NOW to encrypt all message traffic, manages internal data queues to make sure messages are not dropped, and automatically calibrates the WiFi channel used by ESP-NOW to match whatever is needed by any devices that are also connected to HomeKit through your central WiFi network. +SpanPoint creates all the internal data queues needed to manage message flow, configures ESP-NOW to encrypt all message traffic, and auto-sets the WiFi channel used by ESP-NOW for transmission to match whatever is needed by any devices that are also connected to HomeKit through your central WiFi network. SpanPoint is part of the main HomeSpan library and is accessible by adding `#include "HomeSpan.h"` near the top of your sketch. Detailed descriptions of the SpanPoint class and all of its methods are provided below. ## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* -Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "corresponding" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "complementary" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows: * *macAddress* - the MAC Address of the *other* device to which you want to send data to, and/or receive data from, in the standard 6-byte format "XX:XX:XX:XX:XX:XX", where each XX represents a single 2-digit hexidecimal byte from 00 to FF * *sendSize* - the size, in bytes, of any messages that will be sent from this device to the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `send()` to transmit any messages from this device to the *other* device - * *receiveSize* - the size, in bytes, of any messages that will be received by this device from the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `get()` to retreive any messages transmitted by the *other* device and received on this device - * *queueDepth* - the depth of the queue reserved to hold messages of *receiveSize* bytes that were received by this device from the *other* device, but not yet retreived using `get()`. See below for further details. Default=1 if left unspecified, which should be sufficient for most applications + * *receiveSize* - the size, in bytes, of any messages that will be received by this device from the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `get()` to retreive any messages transmitted by the *other* device to this device + * *queueDepth* - the depth of the queue reserved to hold messages of *receiveSize* bytes that were received by this device from the *other* device, but not yet retreived using `get()`. Default=1 if left unspecified, which should be sufficient for most applications. See `get()` below for further details. -SpanPoint objects created on two separate devices are considered "corresponding" if the MAC Addresses specified in each object reference each other, and the *sendSize* and *receiveSize* of one the SpanPoint object on one device matches the *receiveSize* and *sendSize* of the SpanPoint object on the "other" device, with the exception that it is always okay to set either the *sendSize* or *receiveSize* to zero regardless of the value set on the "other" device. +> SpanPoint objects created on two separate devices are considered "complementary" if the MAC Addresses specified in each SpanPoint object references each other's devices, and the *sendSize* and *receiveSize* of the SpanPoint object on one device matches, respectively, the *receiveSize* and *sendSize* of the SpanPoint object on the *other* device, with the exception that it is always okay to set either the *sendSize* or *receiveSize* to zero regardless of the value set on the *other* device. -SpanPoint will throw a fatal error and halt the sketch if: +SpanPoint will throw a fatal error during instantiation and halt the sketch if: * the *macAddress* specified is ill-formed, or * either *sendSize* or *receiveSize* is set to greater than 200, or * both *sendSize* and *receiveSize* are set to 0, since there is no purpose for a SpanPoint that will neither transmit nor receive data -The following SpanPoint methods are used to transmit and receive messages from a SpanPoint object on one device to a corresponding SpanPoint object on the "other" device: +**The following SpanPoint methods are used to transmit and receive messages from a SpanPoint object on one device to a corresponding SpanPoint object on the *other* device:** * `boolean send(const void *data)` * transmits a message using data pointed to by *data* (which may be a standard data type, such as *uint16_t*, or a user-defined *struct*) to the *other* device * the size of the *data* element to be transmitted much match the *sendSize* parameter specified when the SpanPoint object was created - * returns **true** if transmission was successful, otherwise **false** if transmission failed. Note that transmission is considered successful as long as the device can find and connect to the *other* device based on its MAC Address, regardless of whether or not the other device has a corresponding SpanPoint object + * returns **true** if transmission was successful, otherwise **false** if transmission failed. Note that a transmission is considered successful as long as the device can find and connect to the *other* device based on its MAC Address, regardless of whether or not the other device has a corresponding SpanPoint object * `boolean get(void *dataBuf)` @@ -41,9 +39,9 @@ The following SpanPoint methods are used to transmit and receive messages from a * if a message is available, it is **moved** from the internal message queue into *dataBuf* and the method returns **true** * the size of the message will always be equal to the *receiveSize* parameter specified when the SpanPoint object was created, so make sure *dataBuf* is sufficiently sized to store such a message -Note that whether or or not you call the `get()` method, SpanPoint is configured to store in an internal queue any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated. Each SpanPoint object maintains its own internal queue, which can be set to hold more than one message by setting the *queueDepth* parameter to something greater than 1 when instantating the SpanPoint object. +Note that whether or or not you call the `get()` method, SpanPoint is configured to store (in an internal queue) any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated. If the internal queue is full when a message is received, the message is *not* moved to the queue and is instead discarded before it can ever be retreived by the `get()` method. To avoid this, make sure you call `get()` more frequently than you expect to receive messages, or set the *queueDepth* parameter to something greater than 1 when instantating the SpanPoint object. -If the internal queue is full when a message is received, the message is not moved to the queue and will thus never be retrievable by the `get()` method. Regardless of whether or not the queue if full, if the size of a received message does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded. If *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will also be output on the Serial Monitor. +Also note that regardless of whether or not the queue if full, if the size of a received message does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded. If *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will also be output on the Serial Monitor. See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. From 9b67c8917a34bbc875ef77ee32ec414f8ecf413e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 16:12:03 -0500 Subject: [PATCH 42/75] Update NOW.md --- docs/NOW.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/NOW.md b/docs/NOW.md index c89ac36..d040564 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -24,7 +24,7 @@ SpanPoint will throw a fatal error during instantiation and halt the sketch if: * either *sendSize* or *receiveSize* is set to greater than 200, or * both *sendSize* and *receiveSize* are set to 0, since there is no purpose for a SpanPoint that will neither transmit nor receive data -**The following SpanPoint methods are used to transmit and receive messages from a SpanPoint object on one device to a corresponding SpanPoint object on the *other* device:** +**The following SpanPoint methods are used to transmit and receive messages from a SpanPoint object on one device to a complementary SpanPoint object on the *other* device:** * `boolean send(const void *data)` @@ -43,6 +43,32 @@ Note that whether or or not you call the `get()` method, SpanPoint is configured Also note that regardless of whether or not the queue if full, if the size of a received message does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded. If *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will also be output on the Serial Monitor. +**Other methods supported by SpanPoint are as follows:** + +* `uint32_t time()` + + * returns the time elapsed (in millis) since a SpanPoint object last received a valid message + * valid messages are those that can be properly decrypted and whose size matches the *receiveSize* parameter, regardless of whether or not there is room in the queue to store the message + * reading a message in the queue with `get()` has no impact on the elapsed time calculation + * this method is typically used to check whether messages from a transmitting device are overdue (suggesting a potential problem with that device) + +* `static void setPassword(const char *pwd)` + + * this *optional* **class-level** method changes the default passphrase used to generate ESP-NOW encryption keys for all SpanPoint objects from the default passphrase ("HomeSpan") to *pwd*, which can be a character string of any length + * if used, this method must be called *before* the instantiation of any SpanPoint objects. Example: `SpanPoint::setPassword("MyPassword");` + * the same passphrase must be used among all devices that are communicating via SpanPoint, else the receiving device will not be able to decrypt messages it receives + +* `static void setChannelMask(uint16_t mask)` + + * this *optional* **class-level** method changes the default channel bitmask from 0x3FFE (i.e. 0011 1111 1111 1110) to *mask* + * the channel bitmask is used to limit which of the standard channels (1-13) supported by the ESP32 WiFi radio should be tried whenever SpanPoint needs to reset the ESP-NOW channel after a transmission failure + * setting bit number *N* to 1 in the bitmask, where N=[1,13], enables the use of WiFi channel number *N* + * setting bit number *N* to 0 in the bitmask, where N=[1,13], disables the use of WiFi channel number *N* + * example: `SpanPoint::setChannelMask(1<<1 | 1<<6 | 1<<11);` causes SpanPoint to try only WiFi channels 1, 6, and 11 when transmitting messages + * this method will throw a fatal error and halt the sketch if called with a *mask* that does not enable at least one channel + * this method has no effect on SpanPoint if used within a full HomeSpan sketch that connects to HomeKit via the users WiFi network, since under these conditions the WiFi channel must remain set to whatever the WiFi network requires + + See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. --- From fcba5d6debb629fd7107898a8f19523d9068942b Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 16:39:02 -0500 Subject: [PATCH 43/75] Update NOW.md --- docs/NOW.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index d040564..ecd488b 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -66,10 +66,15 @@ Also note that regardless of whether or not the queue if full, if the size of a * setting bit number *N* to 0 in the bitmask, where N=[1,13], disables the use of WiFi channel number *N* * example: `SpanPoint::setChannelMask(1<<1 | 1<<6 | 1<<11);` causes SpanPoint to try only WiFi channels 1, 6, and 11 when transmitting messages * this method will throw a fatal error and halt the sketch if called with a *mask* that does not enable at least one channel - * this method has no effect on SpanPoint if used within a full HomeSpan sketch that connects to HomeKit via the users WiFi network, since under these conditions the WiFi channel must remain set to whatever the WiFi network requires - + * this method has no effect on SpanPoint if used within a full HomeSpan sketch that connects to HomeKit via a central WiFi network, since under these conditions the WiFi channel must remain set to whatever the central WiFi network requires -See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. +One of the primary reasons for using SpanPoint is to enable the use of battery-powered devices. Since HomeKit requires an always-on WiFi connection, wall-power is a must. But ESP-NOW does not require always-on connectivity to a central WiFi network, which makes it possible to power things like "remote sensor" devices with just a battery, and have those devices transmit their measurement via SpanPoint to a central device that runs a full HomeSpan sketch and connects to HomeKit. + +Examples showing one such "remote sensor" configuration be done can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples →RemoteSensors*](../Other%20Examples/RemoteSensors). This folder contains three sketches: + +* *MainDevice.ino* - a full HomeSpan sketch the implements 2 Temperature Sensor Accessories using SpanPoint to read messages containing temperature updates from remote devices +* *RemoteDevice.ino* - a lightweight sketch that simulates taking a temperature measurement every 20 seconds and transmitting the result to the Main Device via SpanPoint +* *RemoteTempSensor.ino* - a lightweight sketch that is similar to *RemoteDevice.ino*, except that instead of simulating a temperature sensor, it implements an actual Adafruit ADT7410 I2C-based temperature sensor. This sketch also uses some power-management techniques, such as lowering the CPU frequency and entering into deep-sleep after each measurement is taken --- From 7a5a2dc255bb78df2d7588fc07b93c8856ab0007 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 16:51:16 -0500 Subject: [PATCH 44/75] Update NOW.md --- docs/NOW.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/NOW.md b/docs/NOW.md index ecd488b..5d7d632 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -68,13 +68,15 @@ Also note that regardless of whether or not the queue if full, if the size of a * this method will throw a fatal error and halt the sketch if called with a *mask* that does not enable at least one channel * this method has no effect on SpanPoint if used within a full HomeSpan sketch that connects to HomeKit via a central WiFi network, since under these conditions the WiFi channel must remain set to whatever the central WiFi network requires -One of the primary reasons for using SpanPoint is to enable the use of battery-powered devices. Since HomeKit requires an always-on WiFi connection, wall-power is a must. But ESP-NOW does not require always-on connectivity to a central WiFi network, which makes it possible to power things like "remote sensor" devices with just a battery, and have those devices transmit their measurement via SpanPoint to a central device that runs a full HomeSpan sketch and connects to HomeKit. +## Typical Use Cases -Examples showing one such "remote sensor" configuration be done can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples →RemoteSensors*](../Other%20Examples/RemoteSensors). This folder contains three sketches: +One of the primary reasons for using SpanPoint is to enable the deployement of battery-powered devices. Since HomeKit requires an always-on WiFi connection, wall-power is a must. But ESP-NOW does not require always-on connectivity to a central WiFi network, which makes it possible to power things like remote-sensor devices with just a battery. Such battery-powered "Remote Devices" can take periodic local measurements and transmit them via SpanPoint messages to a wall-powered "Main Device" that is running a full HomeSpan sketch connected to HomeKit via a central WiFi network. -* *MainDevice.ino* - a full HomeSpan sketch the implements 2 Temperature Sensor Accessories using SpanPoint to read messages containing temperature updates from remote devices -* *RemoteDevice.ino* - a lightweight sketch that simulates taking a temperature measurement every 20 seconds and transmitting the result to the Main Device via SpanPoint -* *RemoteTempSensor.ino* - a lightweight sketch that is similar to *RemoteDevice.ino*, except that instead of simulating a temperature sensor, it implements an actual Adafruit ADT7410 I2C-based temperature sensor. This sketch also uses some power-management techniques, such as lowering the CPU frequency and entering into deep-sleep after each measurement is taken +Examples showing such a configuration can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteSensors*](../Other%20Examples/RemoteSensors). This folder contains three sketches: + +* *MainDevice.ino* - a full HomeSpan sketch that implements two Temperature Sensor Accessories, but instead of taking its own temperature measurements, it uses SpanPoint to read messages containing temperature updates from other Remote Devices +* *RemoteDevice.ino* - a lightweight sketch that simulates taking periodic temperature measurements, which are then transmitted to the Main Device via SpanPoint +* *RemoteTempSensor.ino* - a lightweight sketch that is similar to *RemoteDevice.ino*, except that instead of simulating a temperature sensor, it implements an actual Adafruit ADT7410 I2C-based temperature sensor. This sketch also uses some power-management techniques to extend battery life, such as lowering the CPU frequency and entering into deep-sleep after each measurement is taken --- From 74bbde2492ccfa5fe9e112ff28d3a6c767dd46db Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 16 Oct 2022 16:53:11 -0500 Subject: [PATCH 45/75] Changed SpanPoint::send(void *data) to SpanPoint::send(const void *data) --- src/HomeSpan.cpp | 2 +- src/HomeSpan.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 003601e..62ee37c 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -2279,7 +2279,7 @@ boolean SpanPoint::get(void *dataBuf){ /////////////////////////////// -boolean SpanPoint::send(void *data){ +boolean SpanPoint::send(const void *data){ if(sendSize==0) return(false); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 3312d42..627bf96 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -784,7 +784,7 @@ class SpanPoint { static void setPassword(const char *pwd){init(pwd);}; static void setChannelMask(uint16_t mask); boolean get(void *dataBuf); - boolean send(void *data); + boolean send(const void *data); uint32_t time(){return(millis()-receiveTime);} }; From edafc09cfc1ce01c12a02b7fa859cc671f54bb33 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 20:35:22 -0500 Subject: [PATCH 46/75] Update Tutorials.md --- docs/Tutorials.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/Tutorials.md b/docs/Tutorials.md index 6ea8fd0..d321446 100644 --- a/docs/Tutorials.md +++ b/docs/Tutorials.md @@ -132,6 +132,9 @@ Demonstrates how to create Custom Services and Custom Characteristics in HomeSpa ### [ProgrammableHub](../Other%20Examples/ProgrammableHub) Demonstrates how to implement a fully programmable Light Accessory Hub that allows the user to *dynamically* add/delete up to 12 Light Accessories directly through a device-hosted *web interface* or via HomeSpan's *command-line inteface*. Each light can be configured as dimmable/non-dimmable with either no color control, full RGB color control, or color-temperature control. Builds upon many of the techniques used in [Example 20](../examples/20-AdvancedTechniques) +### [RemoteSensors](../Other%20Examples/RemoteSensors) +Demonstrates how SpanPoint can be used to transmit messages from battery-powered Remote Devices running light-weight sketches that measure the local temperature, to a wall-powered Main Device running a full HomeSpan sketch implementing Temperature Sensor Accessories. See [SpanPoint: Point-to-Point Communication between ESP32 Devices](NOW.md) for full details regarding the SpanPoint class and all of its methods. + --- [↩️](README.md) Back to the Welcome page From e8d662671e1c01210abd40290ac84b6ac2b7029a Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 21:04:37 -0500 Subject: [PATCH 47/75] Update README.md --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index 9635e1e..c106be9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -100,6 +100,7 @@ HomeSpan includes the following documentation: * [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral * [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral * [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips +* [HomeSpan SpanPoint](https://github.com/HomeSpan/HomeSpan/blob/dev/docs/Pixels.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW * [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics * [HomeSpan Message Logging](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page * [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library From 15249230983fb1f432f202ea242d59497f8c9377 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 21:05:22 -0500 Subject: [PATCH 48/75] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index c106be9..d922193 100644 --- a/docs/README.md +++ b/docs/README.md @@ -100,7 +100,7 @@ HomeSpan includes the following documentation: * [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral * [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral * [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips -* [HomeSpan SpanPoint](https://github.com/HomeSpan/HomeSpan/blob/dev/docs/Pixels.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW +* [HomeSpan SpanPoint](https://github.com/HomeSpan/HomeSpan/blob/dev/docs/NOW.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW * [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics * [HomeSpan Message Logging](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page * [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library From 15fe92519ad65d009313cfaebc2437c6422a10c9 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 21:06:47 -0500 Subject: [PATCH 49/75] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index d922193..339c85b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -100,7 +100,7 @@ HomeSpan includes the following documentation: * [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral * [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral * [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips -* [HomeSpan SpanPoint](https://github.com/HomeSpan/HomeSpan/blob/dev/docs/NOW.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW +* [HomeSpan SpanPoint](https://github.com/HomeSpan/HomeSpan/blob/master/docs/NOW.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW * [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics * [HomeSpan Message Logging](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page * [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library From 078ffa35ed037a2c023e813a8962265873f1fdc7 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 16 Oct 2022 21:10:19 -0500 Subject: [PATCH 50/75] Update README.md --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index 339c85b..3fbd7cd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -31,6 +31,7 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht * Touch pads/sensors connected to an ESP32 pin (for ESP32 devices that support touch pads) * Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals * Dedicated classes to control one- and two-wire addressable RGB and RGBW LEDs and LED strips +* Dedicated class that faciliates seemless point-to-point communication between ESP32 devices using ESP-NOW * Integrated Web Log for user-defined log messages * Extensively-commented Tutorial Sketches taking you from the very basics of HomeSpan through advanced HomeKit topics * Additional examples and projects showcasing real-world implementations of HomeSpan From f8be2847bd7c6b8c7e6471001f4b2e83bb854b57 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 22 Oct 2022 15:25:31 -0500 Subject: [PATCH 51/75] Fixed bug in touch sensor logic that would cause failure to compile under 2.0.0-2.0.2 Verified that Unit Test compiles under all version 2.0.0 through 2.0.5 and works on 2.0.0 as well as 2.0.5 --- src/Utils.cpp | 4 +--- src/Utils.h | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index 21f57b4..4fd4c68 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -222,6 +222,4 @@ void PushButton::reset(){ ////////////////////////////////////// -#if SOC_TOUCH_SENSOR_NUM > 0 - touch_value_t PushButton::threshold=0; -#endif +touch_value_t PushButton::threshold=0; diff --git a/src/Utils.h b/src/Utils.h index 2883465..ecac65e 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -71,6 +71,12 @@ struct TempBuffer { // PushButton // //////////////////////////////// +#if SOC_TOUCH_VERSION_2 +typedef uint32_t touch_value_t; +#else +typedef uint16_t touch_value_t; +#endif + class PushButton{ int status; @@ -79,11 +85,9 @@ class PushButton{ uint32_t doubleAlarm; uint32_t longAlarm; int pressType; - -#if SOC_TOUCH_SENSOR_NUM > 0 + static touch_value_t threshold; static const int calibCount=20; -#endif protected: @@ -103,10 +107,12 @@ class PushButton{ static boolean TRIGGER_ON_LOW(int pin){return(!digitalRead(pin));} static boolean TRIGGER_ON_HIGH(int pin){return(digitalRead(pin));} -#if SOC_TOUCH_VERSION_1 // ESP32 - static boolean TRIGGER_ON_TOUCH(int pin){return(touchRead(pin) 0 +#if SOC_TOUCH_VERSION_2 static boolean TRIGGER_ON_TOUCH(int pin){return(touchRead(pin)>threshold);} +#else + static boolean TRIGGER_ON_TOUCH(int pin){return(touchRead(pin) Date: Sun, 23 Oct 2022 21:26:41 -0400 Subject: [PATCH 52/75] Started homeSpan.setEventCallback() --- src/HomeSpan.cpp | 8 ++++++-- src/HomeSpan.h | 9 +++++++++ src/src.ino | 8 ++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 62ee37c..d987953 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -195,9 +195,13 @@ void Span::pollTask() { } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); statusLED->start(LED_WIFI_NEEDED); + if(hsEventCallback) + hsEventCallback(HS_WIFI_NEEDED); } } else { statusLED->start(LED_WIFI_CONNECTING); + if(hsEventCallback) + hsEventCallback(HS_WIFI_CONNECTING); } if(controlButton) @@ -328,8 +332,8 @@ int Span::getFreeSlot(){ void Span::commandMode(){ - if(!statusDevice){ - Serial.print("*** ERROR: CAN'T ENTER COMMAND MODE WITHOUT A DEFINED STATUS LED***\n\n"); + if(!statusDevice && !hsEventCallback){ + Serial.print("*** ERROR: CAN'T ENTER COMMAND MODE WITHOUT A DEFINED STATUS LED OR EVENT HANDLER CALLBACK***\n\n"); return; } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 627bf96..0e5a75c 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -67,6 +67,13 @@ enum { /////////////////////////////// +enum HS_EVENT { + HS_WIFI_NEEDED, + HS_WIFI_CONNECTING +}; + +/////////////////////////////// + // Forward-Declarations struct Span; @@ -203,6 +210,7 @@ class Span{ void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false) boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found void (*apFunction)()=NULL; // optional function to invoke when starting Access Point + void (*hsEventCallback)(HS_EVENT hsEvent)=NULL; // optional callback for various HomeSpan events WiFiServer *hapServer; // pointer to the HAP Server connection Blinker *statusLED; // indicates HomeSpan status @@ -284,6 +292,7 @@ class Span{ void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials + void setEventCallback(void (*f)(HS_EVENT hsEvent)){hsEventCallback=f;} // sets an optional user-defined function to call for various HomeSpan Events void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS diff --git a/src/src.ino b/src/src.ino index b66e78d..cac8e34 100644 --- a/src/src.ino +++ b/src/src.ino @@ -44,6 +44,8 @@ void setup() { homeSpan.setSketchVersion("OTA Test 8"); homeSpan.setWifiCallback(wifiEstablished); + homeSpan.setEventCallback(hsEvents); + new SpanUserCommand('d',"- My Description",userCom1); new SpanUserCommand('e',"- My second Description",userCom2); @@ -162,3 +164,9 @@ void userCom1(const char *v){ void userCom2(const char *v){ Serial.printf("In User Command 2: '%s'\n\n",v); } + +////////////////////////////////////// + +void hsEvents(HS_EVENT event){ + Serial.printf("\n*** HOMESPAN EVENT: %d\n",event); +} From 4e23c5e357436b4d7de5fa58b5696d688d27b7bb Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 24 Oct 2022 06:46:04 -0400 Subject: [PATCH 53/75] Updating hsEventCallback with more events --- src/HomeSpan.cpp | 42 ++++++++++++++++++++++++++++++++++-------- src/HomeSpan.h | 9 ++++++++- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index d987953..42f3ddd 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -341,7 +341,8 @@ void Span::commandMode(){ int mode=1; boolean done=false; statusLED->start(500,0.3,mode,1000); - + if(hsEventCallback) + hsEventCallback(HS_CONFIG_MODE_EXIT); unsigned long alarmTime=millis()+comModeLife; while(!done){ @@ -360,6 +361,8 @@ void Span::commandMode(){ if(mode==6) mode=1; statusLED->start(500,0.3,mode,1000); + if(hsEventCallback) + hsEventCallback((HS_EVENT)(HS_CONFIG_MODE_EXIT+mode-1)); } else { done=true; } @@ -373,13 +376,28 @@ void Span::commandMode(){ case 1: Serial.print("*** NO ACTION\n\n"); - if(strlen(network.wifiData.ssid)==0) + if(strlen(network.wifiData.ssid)==0){ statusLED->start(LED_WIFI_NEEDED); + if(hsEventCallback) + hsEventCallback(HS_WIFI_NEEDED); + } else - if(!HAPClient::nAdminControllers()) + if(WiFi.status()!=WL_CONNECTED){ + statusLED->start(LED_WIFI_CONNECTING); + if(hsEventCallback) + hsEventCallback(HS_WIFI_CONNECTING); + } + else + if(!HAPClient::nAdminControllers()){ statusLED->start(LED_PAIRING_NEEDED); - else + if(hsEventCallback) + hsEventCallback(HS_PAIRING_NEEDED); + } + else{ statusLED->on(); + if(hsEventCallback) + hsEventCallback(HS_PAIRED); + } break; case 2: @@ -411,12 +429,14 @@ void Span::checkConnect(){ if(WiFi.status()==WL_CONNECTED) return; - addWebLog(true,"*** WiFi Connection Lost!"); // losing and re-establishing connection has not been tested + addWebLog(true,"*** WiFi Connection Lost!"); connected++; waitTime=60000; alarmConnect=0; statusLED->start(LED_WIFI_CONNECTING); - } + if(hsEventCallback) + hsEventCallback(HS_WIFI_CONNECTING); + } if(WiFi.status()!=WL_CONNECTED){ if(millis()start(LED_PAIRING_NEEDED); - else + if(hsEventCallback) + hsEventCallback(HS_PAIRING_NEEDED); + } + else{ statusLED->on(); + if(hsEventCallback) + hsEventCallback(HS_PAIRED); + } connected++; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 0e5a75c..156695d 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -69,7 +69,14 @@ enum { enum HS_EVENT { HS_WIFI_NEEDED, - HS_WIFI_CONNECTING + HS_WIFI_CONNECTING, + HS_PAIRING_NEEDED, + HS_PAIRED, + HS_CONFIG_MODE_EXIT, + HS_CONFIG_MODE_REBOOT, + HS_CONFIG_MODE_LAUNCH_AP, + HS_CONFIG_MODE_UNPAIR, + HS_CONFIG_MODE_ERASE_WIFI }; /////////////////////////////// From 7ff91ccf0256a77a1354bf43454a37a792322e85 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 24 Oct 2022 19:03:15 -0400 Subject: [PATCH 54/75] Changed hsEventCallback to statusCallback --- src/HomeSpan.cpp | 89 ++++++++++++++++++++++-------------------------- src/HomeSpan.h | 7 ++-- src/src.ino | 6 ++-- 3 files changed, 48 insertions(+), 54 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 42f3ddd..aad5797 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -195,13 +195,13 @@ void Span::pollTask() { } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); statusLED->start(LED_WIFI_NEEDED); - if(hsEventCallback) - hsEventCallback(HS_WIFI_NEEDED); + if(statusCallback) + statusCallback(HS_WIFI_NEEDED); } } else { statusLED->start(LED_WIFI_CONNECTING); - if(hsEventCallback) - hsEventCallback(HS_WIFI_CONNECTING); + if(statusCallback) + statusCallback(HS_WIFI_CONNECTING); } if(controlButton) @@ -332,7 +332,7 @@ int Span::getFreeSlot(){ void Span::commandMode(){ - if(!statusDevice && !hsEventCallback){ + if(!statusDevice && !statusCallback){ Serial.print("*** ERROR: CAN'T ENTER COMMAND MODE WITHOUT A DEFINED STATUS LED OR EVENT HANDLER CALLBACK***\n\n"); return; } @@ -341,8 +341,8 @@ void Span::commandMode(){ int mode=1; boolean done=false; statusLED->start(500,0.3,mode,1000); - if(hsEventCallback) - hsEventCallback(HS_CONFIG_MODE_EXIT); + if(statusCallback) + statusCallback(HS_CONFIG_MODE_EXIT); unsigned long alarmTime=millis()+comModeLife; while(!done){ @@ -361,8 +361,8 @@ void Span::commandMode(){ if(mode==6) mode=1; statusLED->start(500,0.3,mode,1000); - if(hsEventCallback) - hsEventCallback((HS_EVENT)(HS_CONFIG_MODE_EXIT+mode-1)); + if(statusCallback) + statusCallback((HS_STATUS)(HS_CONFIG_MODE_EXIT+mode-1)); } else { done=true; } @@ -376,28 +376,7 @@ void Span::commandMode(){ case 1: Serial.print("*** NO ACTION\n\n"); - if(strlen(network.wifiData.ssid)==0){ - statusLED->start(LED_WIFI_NEEDED); - if(hsEventCallback) - hsEventCallback(HS_WIFI_NEEDED); - } - else - if(WiFi.status()!=WL_CONNECTED){ - statusLED->start(LED_WIFI_CONNECTING); - if(hsEventCallback) - hsEventCallback(HS_WIFI_CONNECTING); - } - else - if(!HAPClient::nAdminControllers()){ - statusLED->start(LED_PAIRING_NEEDED); - if(hsEventCallback) - hsEventCallback(HS_PAIRING_NEEDED); - } - else{ - statusLED->on(); - if(hsEventCallback) - hsEventCallback(HS_PAIRED); - } + resetStatus(); break; case 2: @@ -434,8 +413,8 @@ void Span::checkConnect(){ waitTime=60000; alarmConnect=0; statusLED->start(LED_WIFI_CONNECTING); - if(hsEventCallback) - hsEventCallback(HS_WIFI_CONNECTING); + if(statusCallback) + statusCallback(HS_WIFI_CONNECTING); } if(WiFi.status()!=WL_CONNECTED){ @@ -462,17 +441,7 @@ void Span::checkConnect(){ return; } - if(!HAPClient::nAdminControllers()){ - statusLED->start(LED_PAIRING_NEEDED); - if(hsEventCallback) - hsEventCallback(HS_PAIRING_NEEDED); - } - else{ - statusLED->on(); - if(hsEventCallback) - hsEventCallback(HS_PAIRED); - } - + resetStatus(); connected++; addWebLog(true,"WiFi Connected! IP Address = %s",WiFi.localIP().toString().c_str()); @@ -796,10 +765,7 @@ void Span::processSerialCommand(const char *c){ Serial.print("\nDEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - if(strlen(network.wifiData.ssid)==0) - statusLED->start(LED_WIFI_NEEDED); - else - statusLED->start(LED_PAIRING_NEEDED); + resetStatus(); } break; @@ -1150,6 +1116,33 @@ void Span::processSerialCommand(const char *c){ /////////////////////////////// +void Span::resetStatus(){ + if(strlen(network.wifiData.ssid)==0){ + statusLED->start(LED_WIFI_NEEDED); + if(statusCallback) + statusCallback(HS_WIFI_NEEDED); + } + else + if(WiFi.status()!=WL_CONNECTED){ + statusLED->start(LED_WIFI_CONNECTING); + if(statusCallback) + statusCallback(HS_WIFI_CONNECTING); + } + else + if(!HAPClient::nAdminControllers()){ + statusLED->start(LED_PAIRING_NEEDED); + if(statusCallback) + statusCallback(HS_PAIRING_NEEDED); + } + else{ + statusLED->on(); + if(statusCallback) + statusCallback(HS_PAIRED); + } +} + +/////////////////////////////// + void Span::setWifiCredentials(const char *ssid, const char *pwd){ sprintf(network.wifiData.ssid,"%.*s",MAX_SSID,ssid); sprintf(network.wifiData.pwd,"%.*s",MAX_PWD,pwd); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 156695d..dad6391 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -67,7 +67,7 @@ enum { /////////////////////////////// -enum HS_EVENT { +enum HS_STATUS { HS_WIFI_NEEDED, HS_WIFI_CONNECTING, HS_PAIRING_NEEDED, @@ -217,7 +217,7 @@ class Span{ void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false) boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found void (*apFunction)()=NULL; // optional function to invoke when starting Access Point - void (*hsEventCallback)(HS_EVENT hsEvent)=NULL; // optional callback for various HomeSpan events + void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes WiFiServer *hapServer; // pointer to the HAP Server connection Blinker *statusLED; // indicates HomeSpan status @@ -241,6 +241,7 @@ class Span{ int getFreeSlot(); // returns free HAPClient slot number. HAPClients slot keep track of each active HAPClient connection void checkConnect(); // check WiFi connection; connect if needed void commandMode(); // allows user to control and reset HomeSpan settings with the control button + void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status int sprintfAttributes(char *cBuf, int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator @@ -299,7 +300,7 @@ class Span{ void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials - void setEventCallback(void (*f)(HS_EVENT hsEvent)){hsEventCallback=f;} // sets an optional user-defined function to call for various HomeSpan Events + void setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;} // sets an optional user-defined function to call when HomeSpan status changes void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS diff --git a/src/src.ino b/src/src.ino index cac8e34..5a00e4a 100644 --- a/src/src.ino +++ b/src/src.ino @@ -44,7 +44,7 @@ void setup() { homeSpan.setSketchVersion("OTA Test 8"); homeSpan.setWifiCallback(wifiEstablished); - homeSpan.setEventCallback(hsEvents); + homeSpan.setStatusCallback(statusUpdate); new SpanUserCommand('d',"- My Description",userCom1); new SpanUserCommand('e',"- My second Description",userCom2); @@ -167,6 +167,6 @@ void userCom2(const char *v){ ////////////////////////////////////// -void hsEvents(HS_EVENT event){ - Serial.printf("\n*** HOMESPAN EVENT: %d\n",event); +void statusUpdate(HS_STATUS status){ + Serial.printf("\n*** HOMESPAN STATUS CHANGE: %d\n",status); } From bb1b599797ff076246fee615141c6cd98cb488bd Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 24 Oct 2022 21:48:37 -0400 Subject: [PATCH 55/75] Completed homeSpan.setStatusCallback() To Do: create documentation --- src/HomeSpan.cpp | 8 +++++++- src/HomeSpan.h | 5 ++++- src/Network.cpp | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index aad5797..5b1e4fb 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -814,8 +814,10 @@ void Span::processSerialCommand(const char *c){ Serial.print("*** Setup Code Unchanged\n"); } - Serial.print("\n*** Re-starting ***\n\n"); statusLED->off(); + if(statusCallback) + statusCallback(HS_REBOOTING); + Serial.print("\n*** Re-starting ***\n\n"); delay(1000); ESP.restart(); // re-start device } @@ -824,6 +826,8 @@ void Span::processSerialCommand(const char *c){ case 'X': { statusLED->off(); + if(statusCallback) + statusCallback(HS_WIFI_ERASED); nvs_erase_all(wifiNVS); nvs_commit(wifiNVS); WiFi.begin("none"); @@ -855,6 +859,8 @@ void Span::processSerialCommand(const char *c){ case 'R': { statusLED->off(); + if(statusCallback) + statusCallback(HS_REBOOTING); Serial.print("\n*** Restarting...\n\n"); delay(1000); ESP.restart(); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index dad6391..69e7c8b 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -76,7 +76,10 @@ enum HS_STATUS { HS_CONFIG_MODE_REBOOT, HS_CONFIG_MODE_LAUNCH_AP, HS_CONFIG_MODE_UNPAIR, - HS_CONFIG_MODE_ERASE_WIFI + HS_CONFIG_MODE_ERASE_WIFI, + HS_REBOOTING, + HS_WIFI_ERASED, + HS_AP_STARTED }; /////////////////////////////// diff --git a/src/Network.cpp b/src/Network.cpp index 9a3dbc2..6467089 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -117,7 +117,9 @@ void Network::apConfigure(){ Serial.print("\n"); homeSpan.statusLED->start(LED_AP_STARTED); - + if(homeSpan.statusCallback) + homeSpan.statusCallback(HS_AP_STARTED); + Serial.print("\nScanning for Networks...\n\n"); scan(); // scan for networks From d55dad152973a4b28af55cab302c0d1aded5f1e6 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 29 Oct 2022 07:37:34 -0500 Subject: [PATCH 56/75] Added more events to statusCallback() --- src/HAP.cpp | 5 +++++ src/HomeSpan.cpp | 8 ++++++-- src/HomeSpan.h | 5 ++++- src/Network.cpp | 11 +++++++++-- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index 4b163af..b747ac5 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -640,6 +640,9 @@ int HAPClient::postPairSetupURL(){ homeSpan.statusLED->on(); + if(homeSpan.statusCallback) + homeSpan.statusCallback(HS_PAIRED); + if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(true); @@ -1638,6 +1641,8 @@ void HAPClient::removeController(uint8_t *id){ LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) homeSpan.statusLED->start(LED_PAIRING_NEEDED); + if(homeSpan.statusCallback) + homeSpan.statusCallback(HS_PAIRING_NEEDED); if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(false); } diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 5b1e4fb..baffbe4 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -878,7 +878,9 @@ void Span::processSerialCommand(const char *c){ nvs_commit(charNVS); nvs_erase_all(otaNVS); nvs_commit(otaNVS); - WiFi.begin("none"); + WiFi.begin("none"); + if(statusCallback) + statusCallback(HS_FACTORY_RESET); Serial.print("\n*** FACTORY RESET! Restarting...\n\n"); delay(1000); ESP.restart(); @@ -2139,6 +2141,8 @@ void SpanOTA::start(){ esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); otaPercent=0; homeSpan.statusLED->start(LED_OTA_STARTED); + if(homeSpan.statusCallback) + homeSpan.statusCallback(HS_OTA_STARTED); } /////////////////////////////// @@ -2148,7 +2152,7 @@ void SpanOTA::end(){ nvs_commit(homeSpan.otaNVS); Serial.printf(" DONE! Rebooting...\n"); homeSpan.statusLED->off(); - delay(100); // make sure commit it finished before reboot + delay(100); // make sure commit is finished before reboot } /////////////////////////////// diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 69e7c8b..c3eb23a 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -78,8 +78,11 @@ enum HS_STATUS { HS_CONFIG_MODE_UNPAIR, HS_CONFIG_MODE_ERASE_WIFI, HS_REBOOTING, + HS_FACTORY_RESET, HS_WIFI_ERASED, - HS_AP_STARTED + HS_AP_STARTED, + HS_AP_CONNECTED, + HS_OTA_STARTED }; /////////////////////////////// diff --git a/src/Network.cpp b/src/Network.cpp index 6467089..24f2843 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -276,7 +276,9 @@ void Network::processRequest(char *body, char *formData){ getFormValue(formData,"pwd",wifiData.pwd,MAX_PWD); homeSpan.statusLED->start(LED_WIFI_CONNECTING); - + if(homeSpan.statusCallback) + homeSpan.statusCallback(HS_WIFI_CONNECTING); + responseBody+="" "

Initiating WiFi connection to:

" + String(wifiData.ssid) + "

"; @@ -323,7 +325,9 @@ void Network::processRequest(char *body, char *formData){ } else { homeSpan.statusLED->start(LED_AP_CONNECTED); // slow double-blink - + if(homeSpan.statusCallback) + homeSpan.statusCallback(HS_AP_CONNECTED); + responseBody+="

SUCCESS! Connected to:

" + String(wifiData.ssid) + "

"; responseBody+="

You may enter new 8-digit Setup Code below, or leave blank to retain existing code.

"; @@ -343,6 +347,9 @@ void Network::processRequest(char *body, char *formData){ LOG1("In Landing Page...\n"); homeSpan.statusLED->start(LED_AP_CONNECTED); + if(homeSpan.statusCallback) + homeSpan.statusCallback(HS_AP_CONNECTED); + waitTime=2; responseBody+="

Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.

" From 645c169a5e07a0807557e2e6e17c9f82aba55670 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 29 Oct 2022 11:34:48 -0500 Subject: [PATCH 57/75] created STATUS_UPDATE macro Combines statusLED updates with statusCallback updates --- src/HomeSpan.cpp | 31 ++++++++----------------------- src/HomeSpan.h | 7 +++++-- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index baffbe4..626ae8a 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -194,14 +194,10 @@ void Span::pollTask() { processSerialCommand("A"); } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); - statusLED->start(LED_WIFI_NEEDED); - if(statusCallback) - statusCallback(HS_WIFI_NEEDED); + STATUS_UPDATE(start(LED_WIFI_NEEDED),HS_WIFI_NEEDED); } } else { - statusLED->start(LED_WIFI_CONNECTING); - if(statusCallback) - statusCallback(HS_WIFI_CONNECTING); + STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING); } if(controlButton) @@ -300,11 +296,11 @@ void Span::pollTask() { ArduinoOTA.handle(); if(controlButton && controlButton->primed()) - statusLED->start(LED_ALERT); + STATUS_UPDATE(start(LED_ALERT),HS_ENTERING_CONFIG_MODE); if(controlButton && controlButton->triggered(3000,10000)){ - statusLED->off(); if(controlButton->type()==PushButton::LONG){ + STATUS_UPDATE(off(),HS_FACTORY_RESET); controlButton->wait(); processSerialCommand("F"); // FACTORY RESET } else { @@ -337,12 +333,10 @@ void Span::commandMode(){ return; } - Serial.print("*** ENTERING COMMAND MODE ***\n\n"); + Serial.print("*** COMMAND MODE ***\n\n"); int mode=1; boolean done=false; - statusLED->start(500,0.3,mode,1000); - if(statusCallback) - statusCallback(HS_CONFIG_MODE_EXIT); + STATUS_UPDATE(start(500,0.3,mode,1000),static_cast(HS_ENTERING_CONFIG_MODE+mode)); unsigned long alarmTime=millis()+comModeLife; while(!done){ @@ -352,17 +346,13 @@ void Span::commandMode(){ Serial.print(" seconds).\n\n"); mode=1; done=true; - statusLED->start(LED_ALERT); - delay(2000); } else if(controlButton->triggered(10,3000)){ if(controlButton->type()==PushButton::SINGLE){ mode++; if(mode==6) mode=1; - statusLED->start(500,0.3,mode,1000); - if(statusCallback) - statusCallback((HS_STATUS)(HS_CONFIG_MODE_EXIT+mode-1)); + STATUS_UPDATE(start(500,0.3,mode,1000),static_cast(HS_ENTERING_CONFIG_MODE+mode)); } else { done=true; } @@ -412,9 +402,7 @@ void Span::checkConnect(){ connected++; waitTime=60000; alarmConnect=0; - statusLED->start(LED_WIFI_CONNECTING); - if(statusCallback) - statusCallback(HS_WIFI_CONNECTING); + STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING); } if(WiFi.status()!=WL_CONNECTED){ @@ -869,7 +857,6 @@ void Span::processSerialCommand(const char *c){ case 'F': { - statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); nvs_erase_all(wifiNVS); @@ -879,8 +866,6 @@ void Span::processSerialCommand(const char *c){ nvs_erase_all(otaNVS); nvs_commit(otaNVS); WiFi.begin("none"); - if(statusCallback) - statusCallback(HS_FACTORY_RESET); Serial.print("\n*** FACTORY RESET! Restarting...\n\n"); delay(1000); ESP.restart(); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index c3eb23a..02641e3 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -67,11 +67,14 @@ enum { /////////////////////////////// +#define STATUS_UPDATE(LED_UPDATE,MESSAGE_UPDATE) {statusLED->LED_UPDATE;if(statusCallback)statusCallback(MESSAGE_UPDATE);} + enum HS_STATUS { HS_WIFI_NEEDED, HS_WIFI_CONNECTING, HS_PAIRING_NEEDED, HS_PAIRED, + HS_ENTERING_CONFIG_MODE, HS_CONFIG_MODE_EXIT, HS_CONFIG_MODE_REBOOT, HS_CONFIG_MODE_LAUNCH_AP, @@ -223,7 +226,7 @@ class Span{ void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false) boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found void (*apFunction)()=NULL; // optional function to invoke when starting Access Point - void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes + void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes WiFiServer *hapServer; // pointer to the HAP Server connection Blinker *statusLED; // indicates HomeSpan status @@ -306,7 +309,7 @@ class Span{ void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials - void setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;} // sets an optional user-defined function to call when HomeSpan status changes + void setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;} // sets an optional user-defined function to call when HomeSpan status changes void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS From 05cbc03f12902f0cd0599b19f7f53c5e6df63fe3 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 29 Oct 2022 18:36:30 -0500 Subject: [PATCH 58/75] Added homeSpan.statusString(HS_STATUS statusMessage) Can be used in statusCallback to convert HS_STATUS to char string --- src/HAP.cpp | 13 ++--- src/HomeSpan.cpp | 130 ++++++++++++++++++++++++----------------------- src/HomeSpan.h | 13 +++-- src/Network.cpp | 37 +++++--------- src/src.ino | 2 +- 5 files changed, 94 insertions(+), 101 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index b747ac5..f4c92e7 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -637,11 +637,8 @@ int HAPClient::postPairSetupURL(){ mdns_service_txt_item_set("_hap","_tcp","sf","0"); // broadcast new status LOG1("\n*** ACCESSORY PAIRED! ***\n"); - - homeSpan.statusLED->on(); - - if(homeSpan.statusCallback) - homeSpan.statusCallback(HS_PAIRED); + + STATUS_UPDATE(on(),HS_PAIRED) if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(true); @@ -1640,9 +1637,9 @@ void HAPClient::removeController(uint8_t *id){ removeControllers(); LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - homeSpan.statusLED->start(LED_PAIRING_NEEDED); - if(homeSpan.statusCallback) - homeSpan.statusCallback(HS_PAIRING_NEEDED); + + STATUS_UPDATE(start(LED_PAIRING_NEEDED),HS_PAIRING_NEEDED) + if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(false); } diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 626ae8a..6dc7039 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -194,10 +194,10 @@ void Span::pollTask() { processSerialCommand("A"); } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); - STATUS_UPDATE(start(LED_WIFI_NEEDED),HS_WIFI_NEEDED); + STATUS_UPDATE(start(LED_WIFI_NEEDED),HS_WIFI_NEEDED) } } else { - STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING); + STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING) } if(controlButton) @@ -296,11 +296,11 @@ void Span::pollTask() { ArduinoOTA.handle(); if(controlButton && controlButton->primed()) - STATUS_UPDATE(start(LED_ALERT),HS_ENTERING_CONFIG_MODE); + STATUS_UPDATE(start(LED_ALERT),HS_ENTERING_CONFIG_MODE) if(controlButton && controlButton->triggered(3000,10000)){ if(controlButton->type()==PushButton::LONG){ - STATUS_UPDATE(off(),HS_FACTORY_RESET); + STATUS_UPDATE(off(),HS_FACTORY_RESET) controlButton->wait(); processSerialCommand("F"); // FACTORY RESET } else { @@ -336,7 +336,7 @@ void Span::commandMode(){ Serial.print("*** COMMAND MODE ***\n\n"); int mode=1; boolean done=false; - STATUS_UPDATE(start(500,0.3,mode,1000),static_cast(HS_ENTERING_CONFIG_MODE+mode)); + STATUS_UPDATE(start(500,0.3,mode,1000),static_cast(HS_ENTERING_CONFIG_MODE+mode)) unsigned long alarmTime=millis()+comModeLife; while(!done){ @@ -352,20 +352,19 @@ void Span::commandMode(){ mode++; if(mode==6) mode=1; - STATUS_UPDATE(start(500,0.3,mode,1000),static_cast(HS_ENTERING_CONFIG_MODE+mode)); + STATUS_UPDATE(start(500,0.3,mode,1000),static_cast(HS_ENTERING_CONFIG_MODE+mode)) } else { done=true; } } // button press } // while - statusLED->start(LED_ALERT); + STATUS_UPDATE(start(LED_ALERT),static_cast(HS_ENTERING_CONFIG_MODE+mode+5)) controlButton->wait(); switch(mode){ case 1: - Serial.print("*** NO ACTION\n\n"); resetStatus(); break; @@ -402,7 +401,7 @@ void Span::checkConnect(){ connected++; waitTime=60000; alarmConnect=0; - STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING); + STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING) } if(WiFi.status()!=WL_CONNECTED){ @@ -753,6 +752,9 @@ void Span::processSerialCommand(const char *c){ Serial.print("\nDEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) + if(homeSpan.pairCallback) + homeSpan.pairCallback(false); + resetStatus(); } break; @@ -770,9 +772,7 @@ void Span::processSerialCommand(const char *c){ nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data nvs_commit(wifiNVS); // commit to NVS Serial.print("\n*** WiFi Credentials SAVED! Re-starting ***\n\n"); - statusLED->off(); - delay(1000); - ESP.restart(); + reboot(); } break; @@ -802,26 +802,18 @@ void Span::processSerialCommand(const char *c){ Serial.print("*** Setup Code Unchanged\n"); } - statusLED->off(); - if(statusCallback) - statusCallback(HS_REBOOTING); Serial.print("\n*** Re-starting ***\n\n"); - delay(1000); - ESP.restart(); // re-start device + reboot(); } break; case 'X': { - - statusLED->off(); - if(statusCallback) - statusCallback(HS_WIFI_ERASED); + nvs_erase_all(wifiNVS); nvs_commit(wifiNVS); WiFi.begin("none"); Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); - delay(1000); - ESP.restart(); // re-start device + reboot(); } break; @@ -835,23 +827,16 @@ void Span::processSerialCommand(const char *c){ case 'H': { - statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); Serial.print("\n*** HomeSpan Device ID and Pairing Data DELETED! Restarting...\n\n"); - delay(1000); - ESP.restart(); + reboot(); } break; case 'R': { - - statusLED->off(); - if(statusCallback) - statusCallback(HS_REBOOTING); - Serial.print("\n*** Restarting...\n\n"); - delay(1000); - ESP.restart(); + + reboot(); } break; @@ -867,18 +852,15 @@ void Span::processSerialCommand(const char *c){ nvs_commit(otaNVS); WiFi.begin("none"); Serial.print("\n*** FACTORY RESET! Restarting...\n\n"); - delay(1000); - ESP.restart(); + reboot(); } break; case 'E': { - statusLED->off(); nvs_flash_erase(); Serial.print("\n*** ALL DATA ERASED! Restarting...\n\n"); - delay(1000); - ESP.restart(); + reboot(); } break; @@ -1110,28 +1092,51 @@ void Span::processSerialCommand(const char *c){ /////////////////////////////// void Span::resetStatus(){ - if(strlen(network.wifiData.ssid)==0){ - statusLED->start(LED_WIFI_NEEDED); - if(statusCallback) - statusCallback(HS_WIFI_NEEDED); - } + if(strlen(network.wifiData.ssid)==0) + STATUS_UPDATE(start(LED_WIFI_NEEDED),HS_WIFI_NEEDED) + else if(WiFi.status()!=WL_CONNECTED) + STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING) + else if(!HAPClient::nAdminControllers()) + STATUS_UPDATE(start(LED_PAIRING_NEEDED),HS_PAIRING_NEEDED) else - if(WiFi.status()!=WL_CONNECTED){ - statusLED->start(LED_WIFI_CONNECTING); - if(statusCallback) - statusCallback(HS_WIFI_CONNECTING); + STATUS_UPDATE(on(),HS_PAIRED) +} + +/////////////////////////////// + +void Span::reboot(){ + STATUS_UPDATE(off(),HS_REBOOTING) + delay(1000); + ESP.restart(); +} + +/////////////////////////////// + +const char* Span::statusString(HS_STATUS s){ + switch(s){ + case HS_WIFI_NEEDED: return("WiFi Credentials Needed"); + case HS_WIFI_CONNECTING: return("WiFi Connecting"); + case HS_PAIRING_NEEDED: return("Device not yet Paired"); + case HS_PAIRED: return("Device Paired"); + case HS_ENTERING_CONFIG_MODE: return("Entering Command Mode"); + case HS_CONFIG_MODE_EXIT: return("1. Exit Command Mode"); + case HS_CONFIG_MODE_REBOOT: return("2. Reboot Device"); + case HS_CONFIG_MODE_LAUNCH_AP: return("3. Launch Access Point"); + case HS_CONFIG_MODE_UNPAIR: return("4. Unpair Device"); + case HS_CONFIG_MODE_ERASE_WIFI: return("5. Erase WiFi Credentials"); + case HS_CONFIG_MODE_EXIT_SELECTED: return("Exiting Command Mode..."); + case HS_CONFIG_MODE_REBOOT_SELECTED: return("Rebooting Device..."); + case HS_CONFIG_MODE_LAUNCH_AP_SELECTED: return("Launching Access Point..."); + case HS_CONFIG_MODE_UNPAIR_SELECTED: return("Unpairing Device..."); + case HS_CONFIG_MODE_ERASE_WIFI_SELECTED: return("Erasing WiFi Credentials..."); + case HS_REBOOTING: return("REBOOTING!"); + case HS_FACTORY_RESET: return("Performing Factory Reset..."); + case HS_AP_STARTED: return("Access Point Started"); + case HS_AP_CONNECTED: return("Access Point Connected"); + case HS_AP_TERMINATED: return("Access Point Terminated"); + case HS_OTA_STARTED: return("OTA Update Started"); + default: return("Unknown"); } - else - if(!HAPClient::nAdminControllers()){ - statusLED->start(LED_PAIRING_NEEDED); - if(statusCallback) - statusCallback(HS_PAIRING_NEEDED); - } - else{ - statusLED->on(); - if(statusCallback) - statusCallback(HS_PAIRED); - } } /////////////////////////////// @@ -2123,11 +2128,9 @@ void SpanOTA::init(boolean _auth, boolean _safeLoad){ void SpanOTA::start(){ Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", - esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); + esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); otaPercent=0; - homeSpan.statusLED->start(LED_OTA_STARTED); - if(homeSpan.statusCallback) - homeSpan.statusCallback(HS_OTA_STARTED); + STATUS_UPDATE(start(LED_OTA_STARTED),HS_OTA_STARTED) } /////////////////////////////// @@ -2136,8 +2139,7 @@ void SpanOTA::end(){ nvs_set_u8(homeSpan.otaNVS,"OTA_REQUIRED",safeLoad); nvs_commit(homeSpan.otaNVS); Serial.printf(" DONE! Rebooting...\n"); - homeSpan.statusLED->off(); - delay(100); // make sure commit is finished before reboot + homeSpan.reboot(); } /////////////////////////////// diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 02641e3..1d6a6c1 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -67,7 +67,7 @@ enum { /////////////////////////////// -#define STATUS_UPDATE(LED_UPDATE,MESSAGE_UPDATE) {statusLED->LED_UPDATE;if(statusCallback)statusCallback(MESSAGE_UPDATE);} +#define STATUS_UPDATE(LED_UPDATE,MESSAGE_UPDATE) {homeSpan.statusLED->LED_UPDATE;if(homeSpan.statusCallback)homeSpan.statusCallback(MESSAGE_UPDATE);} enum HS_STATUS { HS_WIFI_NEEDED, @@ -80,11 +80,16 @@ enum HS_STATUS { HS_CONFIG_MODE_LAUNCH_AP, HS_CONFIG_MODE_UNPAIR, HS_CONFIG_MODE_ERASE_WIFI, + HS_CONFIG_MODE_EXIT_SELECTED, + HS_CONFIG_MODE_REBOOT_SELECTED, + HS_CONFIG_MODE_LAUNCH_AP_SELECTED, + HS_CONFIG_MODE_UNPAIR_SELECTED, + HS_CONFIG_MODE_ERASE_WIFI_SELECTED, HS_REBOOTING, HS_FACTORY_RESET, - HS_WIFI_ERASED, HS_AP_STARTED, HS_AP_CONNECTED, + HS_AP_TERMINATED, HS_OTA_STARTED }; @@ -226,7 +231,8 @@ class Span{ void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false) boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found void (*apFunction)()=NULL; // optional function to invoke when starting Access Point - void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes + void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes + const char* statusString(HS_STATUS s); // returns char string for HomeSpan status change messages WiFiServer *hapServer; // pointer to the HAP Server connection Blinker *statusLED; // indicates HomeSpan status @@ -251,6 +257,7 @@ class Span{ void checkConnect(); // check WiFi connection; connect if needed void commandMode(); // allows user to control and reset HomeSpan settings with the control button void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status + void reboot(); // reboots device int sprintfAttributes(char *cBuf, int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator diff --git a/src/Network.cpp b/src/Network.cpp index 24f2843..9805bed 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -116,9 +116,7 @@ void Network::apConfigure(){ Serial.print(apPassword); Serial.print("\n"); - homeSpan.statusLED->start(LED_AP_STARTED); - if(homeSpan.statusCallback) - homeSpan.statusCallback(HS_AP_STARTED); + STATUS_UPDATE(start(LED_AP_STARTED),HS_AP_STARTED) Serial.print("\nScanning for Networks...\n\n"); @@ -156,11 +154,9 @@ void Network::apConfigure(){ if(homeSpan.controlButton && homeSpan.controlButton->triggered(9999,3000)){ Serial.print("\n*** Access Point Terminated."); - homeSpan.statusLED->start(LED_ALERT); + STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED) homeSpan.controlButton->wait(); - Serial.print(" Restarting... \n\n"); - homeSpan.statusLED->off(); - ESP.restart(); + homeSpan.reboot(); } if(millis()>alarmTimeOut){ @@ -175,13 +171,11 @@ void Network::apConfigure(){ Serial.print(lifetime/1000); Serial.print(" seconds)."); } else { - Serial.print("\n*** Access Point: Configuration Canceled."); + Serial.print("\n*** Access Point: Configuration Cancelled."); } Serial.print(" Restarting...\n\n"); - homeSpan.statusLED->start(LED_ALERT); - delay(1000); - homeSpan.statusLED->off(); - ESP.restart(); + STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED) + homeSpan.reboot(); } } @@ -274,11 +268,9 @@ void Network::processRequest(char *body, char *formData){ getFormValue(formData,"network",wifiData.ssid,MAX_SSID); getFormValue(formData,"pwd",wifiData.pwd,MAX_PWD); - - homeSpan.statusLED->start(LED_WIFI_CONNECTING); - if(homeSpan.statusCallback) - homeSpan.statusCallback(HS_WIFI_CONNECTING); - + + STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING) + responseBody+="" "

Initiating WiFi connection to:

" + String(wifiData.ssid) + "

"; @@ -323,10 +315,8 @@ void Network::processRequest(char *body, char *formData){ WiFi.begin(wifiData.ssid,wifiData.pwd); } else { - - homeSpan.statusLED->start(LED_AP_CONNECTED); // slow double-blink - if(homeSpan.statusCallback) - homeSpan.statusCallback(HS_AP_CONNECTED); + + STATUS_UPDATE(start(LED_AP_CONNECTED),HS_AP_CONNECTED) responseBody+="

SUCCESS! Connected to:

" + String(wifiData.ssid) + "

"; responseBody+="

You may enter new 8-digit Setup Code below, or leave blank to retain existing code.

"; @@ -346,10 +336,7 @@ void Network::processRequest(char *body, char *formData){ LOG1("In Landing Page...\n"); - homeSpan.statusLED->start(LED_AP_CONNECTED); - if(homeSpan.statusCallback) - homeSpan.statusCallback(HS_AP_CONNECTED); - + STATUS_UPDATE(start(LED_AP_CONNECTED),HS_AP_CONNECTED) waitTime=2; responseBody+="

Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.

" diff --git a/src/src.ino b/src/src.ino index 5a00e4a..abf2d6b 100644 --- a/src/src.ino +++ b/src/src.ino @@ -168,5 +168,5 @@ void userCom2(const char *v){ ////////////////////////////////////// void statusUpdate(HS_STATUS status){ - Serial.printf("\n*** HOMESPAN STATUS CHANGE: %d\n",status); + Serial.printf("\n*** HOMESPAN STATUS CHANGE: %s\n",homeSpan.statusString(status)); } From 0929a04270cdcf72c3cd7a708480e4a0c1fb9496 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 29 Oct 2022 18:38:01 -0500 Subject: [PATCH 59/75] Update HomeSpan.h --- src/HomeSpan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 1d6a6c1..5d38a6b 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -232,7 +232,6 @@ class Span{ boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found void (*apFunction)()=NULL; // optional function to invoke when starting Access Point void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes - const char* statusString(HS_STATUS s); // returns char string for HomeSpan status change messages WiFiServer *hapServer; // pointer to the HAP Server connection Blinker *statusLED; // indicates HomeSpan status @@ -317,6 +316,7 @@ class Span{ void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials void setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;} // sets an optional user-defined function to call when HomeSpan status changes + const char* statusString(HS_STATUS s); // returns char string for HomeSpan status change messages void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS From f775a63b7a476b745084717cdd31995dfb282c96 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 30 Oct 2022 08:49:53 -0500 Subject: [PATCH 60/75] Completed and tested all statusCallback and statusString logic --- src/HomeSpan.cpp | 9 +++++---- src/Network.cpp | 2 +- src/Network.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 6dc7039..2c3e9b5 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -771,7 +771,7 @@ void Span::processSerialCommand(const char *c){ network.serialConfigure(); nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data nvs_commit(wifiNVS); // commit to NVS - Serial.print("\n*** WiFi Credentials SAVED! Re-starting ***\n\n"); + Serial.print("\n*** WiFi Credentials SAVED! Restarting ***\n\n"); reboot(); } break; @@ -793,7 +793,7 @@ void Span::processSerialCommand(const char *c){ network.apConfigure(); nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data nvs_commit(wifiNVS); // commit to NVS - Serial.print("\n*** Credentials saved!\n\n"); + Serial.print("\n*** Credentials saved!\n"); if(strlen(network.setupCode)){ char s[10]; sprintf(s,"S%s",network.setupCode); @@ -802,7 +802,8 @@ void Span::processSerialCommand(const char *c){ Serial.print("*** Setup Code Unchanged\n"); } - Serial.print("\n*** Re-starting ***\n\n"); + Serial.print("\n*** Restarting...\n\n"); + STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED) reboot(); } break; @@ -812,7 +813,7 @@ void Span::processSerialCommand(const char *c){ nvs_erase_all(wifiNVS); nvs_commit(wifiNVS); WiFi.begin("none"); - Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); + Serial.print("\n*** WiFi Credentials ERASED! Restarting...\n\n"); reboot(); } break; diff --git a/src/Network.cpp b/src/Network.cpp index 9805bed..7cbaae1 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -153,7 +153,7 @@ void Network::apConfigure(){ while(1){ // loop until we get timed out (which will be accelerated if save/cancel selected) if(homeSpan.controlButton && homeSpan.controlButton->triggered(9999,3000)){ - Serial.print("\n*** Access Point Terminated."); + Serial.print("\n*** Access Point Terminated. Restarting...\n\n"); STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED) homeSpan.controlButton->wait(); homeSpan.reboot(); diff --git a/src/Network.h b/src/Network.h index 663f744..8f43653 100644 --- a/src/Network.h +++ b/src/Network.h @@ -44,7 +44,7 @@ struct Network { const char *apSSID=DEFAULT_AP_SSID; // Access Point SSID const char *apPassword=DEFAULT_AP_PASSWORD; // Access Point password (does not need to be secret - only used to ensure excrypted WiFi connection) - unsigned long lifetime=DEFAULT_AP_TIMEOUT*1000; // length of time (in milliseconds) to keep Access Point alive before shutting down and re-starting + unsigned long lifetime=DEFAULT_AP_TIMEOUT*1000; // length of time (in milliseconds) to keep Access Point alive before shutting down and restarting char **ssidList=NULL; int numSSID; From dd3cc8ab3b37a5f68e30209e41e2eab6916a6833 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 31 Oct 2022 21:08:45 -0500 Subject: [PATCH 61/75] Fixed bug in postPairingsURL logic --- src/HAP.cpp | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index f4c92e7..6505307 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -919,7 +919,7 @@ int HAPClient::postPairingsURL(){ Serial.print("\n*** ERROR: One or more of required 'Identifier,' 'PublicKey,' and 'Permissions' TLV records for this step is bad or missing\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= - tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown (there is no specific error type for missing/bad TLV data) + tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown (there is no specific error type for missing/bad TLV data) break; } @@ -927,32 +927,27 @@ int HAPClient::postPairingsURL(){ Serial.print("\n*** ERROR: Controller making request does not have admin privileges to add/update other Controllers\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= - tlv8.val(kTLVType_Error,tagError_Authentication); // set Error=Authentication + tlv8.val(kTLVType_Error,tagError_Authentication); // set Error=Authentication break; } - if((newCont=findController(tlv8.buf(kTLVType_Identifier)))){ + if((newCont=findController(tlv8.buf(kTLVType_Identifier))) && memcmp(tlv8.buf(kTLVType_PublicKey),newCont->LTPK,32)){ // requested Controller already exists, but LTPKs don't match + Serial.print("\n*** ERROR: Invalid request to update the LTPK of an exsiting Controller\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= - if(!memcmp(cPair->LTPK,newCont->LTPK,32)){ // requested Controller already exists and LTPK matches - newCont->admin=tlv8.val(kTLVType_Permissions)==1?true:false; // update permission of matching Controller - } else { - tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown - } + tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown break; } - - if(!(newCont=getFreeController())){ + + if(!addController(tlv8.buf(kTLVType_Identifier),tlv8.buf(kTLVType_PublicKey),tlv8.val(kTLVType_Permissions)==1?true:false)){ Serial.print("\n*** ERROR: Can't pair more than "); Serial.print(MAX_CONTROLLERS); Serial.print(" Controllers\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= - tlv8.val(kTLVType_Error,tagError_MaxPeers); // set Error=Unknown (there is no specific error type for missing/bad TLV data) - break; + tlv8.val(kTLVType_Error,tagError_MaxPeers); // set Error=MaxPeers + break; } - - addController(tlv8.buf(kTLVType_Identifier),tlv8.buf(kTLVType_PublicKey),tlv8.val(kTLVType_Permissions)==1?true:false); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= @@ -1538,16 +1533,10 @@ void HAPClient::charPrintRow(uint8_t *buf, int n){ Controller *HAPClient::findController(uint8_t *id){ - for(int i=0;i1) - charPrintRow(id,36); - LOG2(controllers[i].admin?" (admin)\n":" (regular)\n"); - return(controllers+i); // return with pointer to matching controller - } - } // loop + for(int i=0;iLTPK,ltpk,32); slot->admin=admin; LOG2("\n*** Updated Controller: "); @@ -1581,7 +1569,7 @@ Controller *HAPClient::addController(uint8_t *id, uint8_t *ltpk, boolean admin){ return(slot); } - if((slot=getFreeController())){ + if((slot=getFreeController())){ // get slot for new controller, if available slot->allocated=true; memcpy(slot->ID,id,36); memcpy(slot->LTPK,ltpk,32); @@ -1593,9 +1581,6 @@ Controller *HAPClient::addController(uint8_t *id, uint8_t *ltpk, boolean admin){ return(slot); } - Serial.print("\n*** WARNING: No open slots. Can't add Controller: "); - hexPrintRow(id,36); - Serial.print(admin?" (admin)\n\n":" (regular)\n\n\n"); return(NULL); } From 326d5c99b5f0cffa0c83c369637713c4fd884357 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 08:07:11 -0400 Subject: [PATCH 62/75] Update Reference.md --- docs/Reference.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index 024f2ca..94eda06 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -144,7 +144,17 @@ The following **optional** `homeSpan` methods enable additional features and pro * sets an optional user-defined callback function, *func*, to be called by HomeSpan upon completion of pairing to a controller (*status=true*) or unpairing from a controller (*status=false*) * this one-time call to *func* is provided for users that would like to trigger additional actions when the device is first paired, or the device is later unpaired * note this *func* is **not** called upon start-up and should not be used to simply check whether a device is paired or unpaired. It is only called when pairing status changes - * the function *func* must be of type *void* and have one *boolean* argument + * the function *func* must be of type *void* and accept one *boolean* argument + +* `void setStatusCallback(void (*func)(HS_STATUS status))` + * sets an optional user-defined callback function, *func*, to be called by HomeSpan whenever its running state (e.g. WiFi Connecting, Pairing Needed...) changes in way that would alter the blinking pattern of the (optional) Status LED + * if *func* is set, it will be called regardless of whether or not a Status LED has actually been defined + * this allows users to reflect changes to the current state of HomeSpan using alternative methods, such as outputting messages to an embedded LCD or E-Ink display + * the function *func* must be of type *void* and accept one argument of enum type *HS_STATUS* + +* `char* statusString(HS_STATUS s)` + * returns a pre-defined character string message representing *s*, which must be of enum type *HS_STATUS* + * typically used in conjunction with `setStatusCallback()` above * `void setPairingCode(const char *s)` * sets the Setup Pairing Code to *s*, which **must** be exactly eight numerical digits (no dashes) From 2746803c3e5121b6a6620b03c6c05a3dae1b9934 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 11:24:25 -0400 Subject: [PATCH 63/75] Create HS_STATUS.md --- docs/HS_STATUS.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/HS_STATUS.md diff --git a/docs/HS_STATUS.md b/docs/HS_STATUS.md new file mode 100644 index 0000000..5d7d632 --- /dev/null +++ b/docs/HS_STATUS.md @@ -0,0 +1,83 @@ +# SpanPoint: Point-to-Point Communication between ESP32 Devices + +SpanPoint is HomeSpan's easy-to-use implementation of the Espressif ESP-NOW protocol. SpanPoint provides bi-directional, point-to-point communication of small, fixed-size messages directly between ESP32 devices based on their MAC Addresses without the need for a central WiFi network. + +To establish connectivity between any two devices simply instantiate a SpanPoint object on each device that references the MAC Address of the other device, as well as specifies the (potentially different) sizes of the messages that each device is expected to send to, and receive from, the other. + +SpanPoint creates all the internal data queues needed to manage message flow, configures ESP-NOW to encrypt all message traffic, and auto-sets the WiFi channel used by ESP-NOW for transmission to match whatever is needed by any devices that are also connected to HomeKit through your central WiFi network. + +SpanPoint is part of the main HomeSpan library and is accessible by adding `#include "HomeSpan.h"` near the top of your sketch. Detailed descriptions of the SpanPoint class and all of its methods are provided below. + +## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* + +Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "complementary" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows: + + * *macAddress* - the MAC Address of the *other* device to which you want to send data to, and/or receive data from, in the standard 6-byte format "XX:XX:XX:XX:XX:XX", where each XX represents a single 2-digit hexidecimal byte from 00 to FF + * *sendSize* - the size, in bytes, of any messages that will be sent from this device to the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `send()` to transmit any messages from this device to the *other* device + * *receiveSize* - the size, in bytes, of any messages that will be received by this device from the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `get()` to retreive any messages transmitted by the *other* device to this device + * *queueDepth* - the depth of the queue reserved to hold messages of *receiveSize* bytes that were received by this device from the *other* device, but not yet retreived using `get()`. Default=1 if left unspecified, which should be sufficient for most applications. See `get()` below for further details. + +> SpanPoint objects created on two separate devices are considered "complementary" if the MAC Addresses specified in each SpanPoint object references each other's devices, and the *sendSize* and *receiveSize* of the SpanPoint object on one device matches, respectively, the *receiveSize* and *sendSize* of the SpanPoint object on the *other* device, with the exception that it is always okay to set either the *sendSize* or *receiveSize* to zero regardless of the value set on the *other* device. + +SpanPoint will throw a fatal error during instantiation and halt the sketch if: + * the *macAddress* specified is ill-formed, or + * either *sendSize* or *receiveSize* is set to greater than 200, or + * both *sendSize* and *receiveSize* are set to 0, since there is no purpose for a SpanPoint that will neither transmit nor receive data + +**The following SpanPoint methods are used to transmit and receive messages from a SpanPoint object on one device to a complementary SpanPoint object on the *other* device:** + +* `boolean send(const void *data)` + + * transmits a message using data pointed to by *data* (which may be a standard data type, such as *uint16_t*, or a user-defined *struct*) to the *other* device + * the size of the *data* element to be transmitted much match the *sendSize* parameter specified when the SpanPoint object was created + * returns **true** if transmission was successful, otherwise **false** if transmission failed. Note that a transmission is considered successful as long as the device can find and connect to the *other* device based on its MAC Address, regardless of whether or not the other device has a corresponding SpanPoint object + +* `boolean get(void *dataBuf)` + + * checks to see if a message has been received into SpanPoint's internal message queue from the *other* device + * if no message is available, the method returns **false** and *dataBuf* is unmodified + * if a message is available, it is **moved** from the internal message queue into *dataBuf* and the method returns **true** + * the size of the message will always be equal to the *receiveSize* parameter specified when the SpanPoint object was created, so make sure *dataBuf* is sufficiently sized to store such a message + +Note that whether or or not you call the `get()` method, SpanPoint is configured to store (in an internal queue) any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated. If the internal queue is full when a message is received, the message is *not* moved to the queue and is instead discarded before it can ever be retreived by the `get()` method. To avoid this, make sure you call `get()` more frequently than you expect to receive messages, or set the *queueDepth* parameter to something greater than 1 when instantating the SpanPoint object. + +Also note that regardless of whether or not the queue if full, if the size of a received message does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded. If *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will also be output on the Serial Monitor. + +**Other methods supported by SpanPoint are as follows:** + +* `uint32_t time()` + + * returns the time elapsed (in millis) since a SpanPoint object last received a valid message + * valid messages are those that can be properly decrypted and whose size matches the *receiveSize* parameter, regardless of whether or not there is room in the queue to store the message + * reading a message in the queue with `get()` has no impact on the elapsed time calculation + * this method is typically used to check whether messages from a transmitting device are overdue (suggesting a potential problem with that device) + +* `static void setPassword(const char *pwd)` + + * this *optional* **class-level** method changes the default passphrase used to generate ESP-NOW encryption keys for all SpanPoint objects from the default passphrase ("HomeSpan") to *pwd*, which can be a character string of any length + * if used, this method must be called *before* the instantiation of any SpanPoint objects. Example: `SpanPoint::setPassword("MyPassword");` + * the same passphrase must be used among all devices that are communicating via SpanPoint, else the receiving device will not be able to decrypt messages it receives + +* `static void setChannelMask(uint16_t mask)` + + * this *optional* **class-level** method changes the default channel bitmask from 0x3FFE (i.e. 0011 1111 1111 1110) to *mask* + * the channel bitmask is used to limit which of the standard channels (1-13) supported by the ESP32 WiFi radio should be tried whenever SpanPoint needs to reset the ESP-NOW channel after a transmission failure + * setting bit number *N* to 1 in the bitmask, where N=[1,13], enables the use of WiFi channel number *N* + * setting bit number *N* to 0 in the bitmask, where N=[1,13], disables the use of WiFi channel number *N* + * example: `SpanPoint::setChannelMask(1<<1 | 1<<6 | 1<<11);` causes SpanPoint to try only WiFi channels 1, 6, and 11 when transmitting messages + * this method will throw a fatal error and halt the sketch if called with a *mask* that does not enable at least one channel + * this method has no effect on SpanPoint if used within a full HomeSpan sketch that connects to HomeKit via a central WiFi network, since under these conditions the WiFi channel must remain set to whatever the central WiFi network requires + +## Typical Use Cases + +One of the primary reasons for using SpanPoint is to enable the deployement of battery-powered devices. Since HomeKit requires an always-on WiFi connection, wall-power is a must. But ESP-NOW does not require always-on connectivity to a central WiFi network, which makes it possible to power things like remote-sensor devices with just a battery. Such battery-powered "Remote Devices" can take periodic local measurements and transmit them via SpanPoint messages to a wall-powered "Main Device" that is running a full HomeSpan sketch connected to HomeKit via a central WiFi network. + +Examples showing such a configuration can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteSensors*](../Other%20Examples/RemoteSensors). This folder contains three sketches: + +* *MainDevice.ino* - a full HomeSpan sketch that implements two Temperature Sensor Accessories, but instead of taking its own temperature measurements, it uses SpanPoint to read messages containing temperature updates from other Remote Devices +* *RemoteDevice.ino* - a lightweight sketch that simulates taking periodic temperature measurements, which are then transmitted to the Main Device via SpanPoint +* *RemoteTempSensor.ino* - a lightweight sketch that is similar to *RemoteDevice.ino*, except that instead of simulating a temperature sensor, it implements an actual Adafruit ADT7410 I2C-based temperature sensor. This sketch also uses some power-management techniques to extend battery life, such as lowering the CPU frequency and entering into deep-sleep after each measurement is taken + +--- + +[↩️](README.md) Back to the Welcome page From 39850c17cb420be45297f334426092110bdc6aa5 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 11:45:29 -0400 Subject: [PATCH 64/75] Update HS_STATUS.md --- docs/HS_STATUS.md | 133 ++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 76 deletions(-) diff --git a/docs/HS_STATUS.md b/docs/HS_STATUS.md index 5d7d632..abc35d1 100644 --- a/docs/HS_STATUS.md +++ b/docs/HS_STATUS.md @@ -1,83 +1,64 @@ -# SpanPoint: Point-to-Point Communication between ESP32 Devices +# HomeSpan Status -SpanPoint is HomeSpan's easy-to-use implementation of the Espressif ESP-NOW protocol. SpanPoint provides bi-directional, point-to-point communication of small, fixed-size messages directly between ESP32 devices based on their MAC Addresses without the need for a central WiFi network. +The optional *homeSpan* method, `void setStatusCallback(void (*func)(HS_STATUS status))`, can be used to create a callback function, *func*, that HomeSpan calls whenever its status changes. HomeSpan passes *func* a single argument, *status*, of type *HS_STATUS*, defined as follows: -To establish connectivity between any two devices simply instantiate a SpanPoint object on each device that references the MAC Address of the other device, as well as specifies the (potentially different) sizes of the messages that each device is expected to send to, and receive from, the other. +```C++ +enum HS_STATUS { + HS_WIFI_NEEDED, + HS_WIFI_CONNECTING, + HS_PAIRING_NEEDED, + HS_PAIRED, + HS_ENTERING_CONFIG_MODE, + HS_CONFIG_MODE_EXIT, + HS_CONFIG_MODE_REBOOT, + HS_CONFIG_MODE_LAUNCH_AP, + HS_CONFIG_MODE_UNPAIR, + HS_CONFIG_MODE_ERASE_WIFI, + HS_CONFIG_MODE_EXIT_SELECTED, + HS_CONFIG_MODE_REBOOT_SELECTED, + HS_CONFIG_MODE_LAUNCH_AP_SELECTED, + HS_CONFIG_MODE_UNPAIR_SELECTED, + HS_CONFIG_MODE_ERASE_WIFI_SELECTED, + HS_REBOOTING, + HS_FACTORY_RESET, + HS_AP_STARTED, + HS_AP_CONNECTED, + HS_AP_TERMINATED, + HS_OTA_STARTED +}; +``` -SpanPoint creates all the internal data queues needed to manage message flow, configures ESP-NOW to encrypt all message traffic, and auto-sets the WiFi channel used by ESP-NOW for transmission to match whatever is needed by any devices that are also connected to HomeKit through your central WiFi network. +The *homeSpan* method `char* statusString(HS_STATUS s)`, is a convenience function for converting any of the above enumerations to short, pre-defined character string messages as follows: -SpanPoint is part of the main HomeSpan library and is accessible by adding `#include "HomeSpan.h"` near the top of your sketch. Detailed descriptions of the SpanPoint class and all of its methods are provided below. - -## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)* - -Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "complementary" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows: - - * *macAddress* - the MAC Address of the *other* device to which you want to send data to, and/or receive data from, in the standard 6-byte format "XX:XX:XX:XX:XX:XX", where each XX represents a single 2-digit hexidecimal byte from 00 to FF - * *sendSize* - the size, in bytes, of any messages that will be sent from this device to the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `send()` to transmit any messages from this device to the *other* device - * *receiveSize* - the size, in bytes, of any messages that will be received by this device from the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `get()` to retreive any messages transmitted by the *other* device to this device - * *queueDepth* - the depth of the queue reserved to hold messages of *receiveSize* bytes that were received by this device from the *other* device, but not yet retreived using `get()`. Default=1 if left unspecified, which should be sufficient for most applications. See `get()` below for further details. - -> SpanPoint objects created on two separate devices are considered "complementary" if the MAC Addresses specified in each SpanPoint object references each other's devices, and the *sendSize* and *receiveSize* of the SpanPoint object on one device matches, respectively, the *receiveSize* and *sendSize* of the SpanPoint object on the *other* device, with the exception that it is always okay to set either the *sendSize* or *receiveSize* to zero regardless of the value set on the *other* device. - -SpanPoint will throw a fatal error during instantiation and halt the sketch if: - * the *macAddress* specified is ill-formed, or - * either *sendSize* or *receiveSize* is set to greater than 200, or - * both *sendSize* and *receiveSize* are set to 0, since there is no purpose for a SpanPoint that will neither transmit nor receive data - -**The following SpanPoint methods are used to transmit and receive messages from a SpanPoint object on one device to a complementary SpanPoint object on the *other* device:** - -* `boolean send(const void *data)` - - * transmits a message using data pointed to by *data* (which may be a standard data type, such as *uint16_t*, or a user-defined *struct*) to the *other* device - * the size of the *data* element to be transmitted much match the *sendSize* parameter specified when the SpanPoint object was created - * returns **true** if transmission was successful, otherwise **false** if transmission failed. Note that a transmission is considered successful as long as the device can find and connect to the *other* device based on its MAC Address, regardless of whether or not the other device has a corresponding SpanPoint object - -* `boolean get(void *dataBuf)` - - * checks to see if a message has been received into SpanPoint's internal message queue from the *other* device - * if no message is available, the method returns **false** and *dataBuf* is unmodified - * if a message is available, it is **moved** from the internal message queue into *dataBuf* and the method returns **true** - * the size of the message will always be equal to the *receiveSize* parameter specified when the SpanPoint object was created, so make sure *dataBuf* is sufficiently sized to store such a message - -Note that whether or or not you call the `get()` method, SpanPoint is configured to store (in an internal queue) any SpanPoint messages it receives from *other* devices, provided that (a) there is room in the internal queue, and (b) the size of the message received matches the *receiveSize* parameter specified when the relevant SpanPoint object was instantiated. If the internal queue is full when a message is received, the message is *not* moved to the queue and is instead discarded before it can ever be retreived by the `get()` method. To avoid this, make sure you call `get()` more frequently than you expect to receive messages, or set the *queueDepth* parameter to something greater than 1 when instantating the SpanPoint object. - -Also note that regardless of whether or not the queue if full, if the size of a received message does not match the *receiveSize* parameter specified for this instance of the SpanPoint object, the message will be discarded. If *receiveSize* is greater than zero, a non-fatal run-time warning about size mismatches will also be output on the Serial Monitor. - -**Other methods supported by SpanPoint are as follows:** - -* `uint32_t time()` - - * returns the time elapsed (in millis) since a SpanPoint object last received a valid message - * valid messages are those that can be properly decrypted and whose size matches the *receiveSize* parameter, regardless of whether or not there is room in the queue to store the message - * reading a message in the queue with `get()` has no impact on the elapsed time calculation - * this method is typically used to check whether messages from a transmitting device are overdue (suggesting a potential problem with that device) - -* `static void setPassword(const char *pwd)` - - * this *optional* **class-level** method changes the default passphrase used to generate ESP-NOW encryption keys for all SpanPoint objects from the default passphrase ("HomeSpan") to *pwd*, which can be a character string of any length - * if used, this method must be called *before* the instantiation of any SpanPoint objects. Example: `SpanPoint::setPassword("MyPassword");` - * the same passphrase must be used among all devices that are communicating via SpanPoint, else the receiving device will not be able to decrypt messages it receives - -* `static void setChannelMask(uint16_t mask)` - - * this *optional* **class-level** method changes the default channel bitmask from 0x3FFE (i.e. 0011 1111 1111 1110) to *mask* - * the channel bitmask is used to limit which of the standard channels (1-13) supported by the ESP32 WiFi radio should be tried whenever SpanPoint needs to reset the ESP-NOW channel after a transmission failure - * setting bit number *N* to 1 in the bitmask, where N=[1,13], enables the use of WiFi channel number *N* - * setting bit number *N* to 0 in the bitmask, where N=[1,13], disables the use of WiFi channel number *N* - * example: `SpanPoint::setChannelMask(1<<1 | 1<<6 | 1<<11);` causes SpanPoint to try only WiFi channels 1, 6, and 11 when transmitting messages - * this method will throw a fatal error and halt the sketch if called with a *mask* that does not enable at least one channel - * this method has no effect on SpanPoint if used within a full HomeSpan sketch that connects to HomeKit via a central WiFi network, since under these conditions the WiFi channel must remain set to whatever the central WiFi network requires - -## Typical Use Cases - -One of the primary reasons for using SpanPoint is to enable the deployement of battery-powered devices. Since HomeKit requires an always-on WiFi connection, wall-power is a must. But ESP-NOW does not require always-on connectivity to a central WiFi network, which makes it possible to power things like remote-sensor devices with just a battery. Such battery-powered "Remote Devices" can take periodic local measurements and transmit them via SpanPoint messages to a wall-powered "Main Device" that is running a full HomeSpan sketch connected to HomeKit via a central WiFi network. - -Examples showing such a configuration can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteSensors*](../Other%20Examples/RemoteSensors). This folder contains three sketches: - -* *MainDevice.ino* - a full HomeSpan sketch that implements two Temperature Sensor Accessories, but instead of taking its own temperature measurements, it uses SpanPoint to read messages containing temperature updates from other Remote Devices -* *RemoteDevice.ino* - a lightweight sketch that simulates taking periodic temperature measurements, which are then transmitted to the Main Device via SpanPoint -* *RemoteTempSensor.ino* - a lightweight sketch that is similar to *RemoteDevice.ino*, except that instead of simulating a temperature sensor, it implements an actual Adafruit ADT7410 I2C-based temperature sensor. This sketch also uses some power-management techniques to extend battery life, such as lowering the CPU frequency and entering into deep-sleep after each measurement is taken +```C++ +const char* Span::statusString(HS_STATUS s){ + switch(s){ + case HS_WIFI_NEEDED: return("WiFi Credentials Needed"); + case HS_WIFI_CONNECTING: return("WiFi Connecting"); + case HS_PAIRING_NEEDED: return("Device not yet Paired"); + case HS_PAIRED: return("Device Paired"); + case HS_ENTERING_CONFIG_MODE: return("Entering Command Mode"); + case HS_CONFIG_MODE_EXIT: return("1. Exit Command Mode"); + case HS_CONFIG_MODE_REBOOT: return("2. Reboot Device"); + case HS_CONFIG_MODE_LAUNCH_AP: return("3. Launch Access Point"); + case HS_CONFIG_MODE_UNPAIR: return("4. Unpair Device"); + case HS_CONFIG_MODE_ERASE_WIFI: return("5. Erase WiFi Credentials"); + case HS_CONFIG_MODE_EXIT_SELECTED: return("Exiting Command Mode..."); + case HS_CONFIG_MODE_REBOOT_SELECTED: return("Rebooting Device..."); + case HS_CONFIG_MODE_LAUNCH_AP_SELECTED: return("Launching Access Point..."); + case HS_CONFIG_MODE_UNPAIR_SELECTED: return("Unpairing Device..."); + case HS_CONFIG_MODE_ERASE_WIFI_SELECTED: return("Erasing WiFi Credentials..."); + case HS_REBOOTING: return("REBOOTING!"); + case HS_FACTORY_RESET: return("Performing Factory Reset..."); + case HS_AP_STARTED: return("Access Point Started"); + case HS_AP_CONNECTED: return("Access Point Connected"); + case HS_AP_TERMINATED: return("Access Point Terminated"); + case HS_OTA_STARTED: return("OTA Update Started"); + default: return("Unknown"); + } +} +``` --- -[↩️](README.md) Back to the Welcome page +[↩️](Reference.md) Back to the Reference API page From 6763b2a79baecfa3a59e8e2f06ae9e8bc3cf0e9a Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 11:48:15 -0400 Subject: [PATCH 65/75] Update HS_STATUS.md --- docs/HS_STATUS.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/HS_STATUS.md b/docs/HS_STATUS.md index abc35d1..7e0267e 100644 --- a/docs/HS_STATUS.md +++ b/docs/HS_STATUS.md @@ -1,6 +1,6 @@ # HomeSpan Status -The optional *homeSpan* method, `void setStatusCallback(void (*func)(HS_STATUS status))`, can be used to create a callback function, *func*, that HomeSpan calls whenever its status changes. HomeSpan passes *func* a single argument, *status*, of type *HS_STATUS*, defined as follows: +The optional ***homeSpan*** method, `void setStatusCallback(void (*func)(HS_STATUS status))`, can be used to create a callback function, *func*, that HomeSpan calls whenever its status changes. HomeSpan passes *func* a single argument, *status*, of type *HS_STATUS*, defined as follows: ```C++ enum HS_STATUS { @@ -28,7 +28,7 @@ enum HS_STATUS { }; ``` -The *homeSpan* method `char* statusString(HS_STATUS s)`, is a convenience function for converting any of the above enumerations to short, pre-defined character string messages as follows: +The ***homeSpan*** method `char* statusString(HS_STATUS s)`, is a convenience function for converting any of the above enumerations to short, pre-defined character string messages as follows: ```C++ const char* Span::statusString(HS_STATUS s){ @@ -59,6 +59,8 @@ const char* Span::statusString(HS_STATUS s){ } ``` +You can of course create any alternative messsages, or take any actions desired, in *func* and do not need to use the pre-defined strings above. + --- [↩️](Reference.md) Back to the Reference API page From 22a004e3153750c1717fc573ac4b7109473a778c Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 11:51:00 -0400 Subject: [PATCH 66/75] Update Reference.md --- docs/Reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 94eda06..6ea8f8d 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -150,10 +150,10 @@ The following **optional** `homeSpan` methods enable additional features and pro * sets an optional user-defined callback function, *func*, to be called by HomeSpan whenever its running state (e.g. WiFi Connecting, Pairing Needed...) changes in way that would alter the blinking pattern of the (optional) Status LED * if *func* is set, it will be called regardless of whether or not a Status LED has actually been defined * this allows users to reflect changes to the current state of HomeSpan using alternative methods, such as outputting messages to an embedded LCD or E-Ink display - * the function *func* must be of type *void* and accept one argument of enum type *HS_STATUS* + * the function *func* must be of type *void* and accept one argument of enum type [HS_STATUS](HS_STATUS.md) * `char* statusString(HS_STATUS s)` - * returns a pre-defined character string message representing *s*, which must be of enum type *HS_STATUS* + * returns a pre-defined character string message representing *s*, which must be of enum type [HS_STATUS](HS_STATUS.md) * typically used in conjunction with `setStatusCallback()` above * `void setPairingCode(const char *s)` From 2ff19dcc30fd24ea920d0a08f6611ed00e882604 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 11:56:07 -0400 Subject: [PATCH 67/75] Update HS_STATUS.md --- docs/HS_STATUS.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/HS_STATUS.md b/docs/HS_STATUS.md index 7e0267e..6b9c7a9 100644 --- a/docs/HS_STATUS.md +++ b/docs/HS_STATUS.md @@ -59,6 +59,25 @@ const char* Span::statusString(HS_STATUS s){ } ``` +Example + +```C++ +#include "HomeSpan.h" + +void setup(){ + homeSpan.setStatusCallback(statusUpdate); // set callback function + ... + homeSpan.begin(); + ... +} + +// create a callback function that simply prints the pre-defined short messages on the Serial Monitor whenever the HomeSpan status changes + +void statusUpdate(HS_STATUS status){ + Serial.printf("\n*** HOMESPAN STATUS CHANGE: %s\n",homeSpan.statusString(status)); +} +``` + You can of course create any alternative messsages, or take any actions desired, in *func* and do not need to use the pre-defined strings above. --- From bdfad4daac72db6760e1dad80b867bb9f8390a44 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 11:57:36 -0400 Subject: [PATCH 68/75] Update HS_STATUS.md --- docs/HS_STATUS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/HS_STATUS.md b/docs/HS_STATUS.md index 6b9c7a9..f516668 100644 --- a/docs/HS_STATUS.md +++ b/docs/HS_STATUS.md @@ -59,7 +59,7 @@ const char* Span::statusString(HS_STATUS s){ } ``` -Example +### Example: ```C++ #include "HomeSpan.h" From 067fd31668ed2e4a4f56558bfa6f937d22a4cdf2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 12:08:56 -0400 Subject: [PATCH 69/75] Update HS_STATUS.md --- docs/HS_STATUS.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/HS_STATUS.md b/docs/HS_STATUS.md index f516668..7f528df 100644 --- a/docs/HS_STATUS.md +++ b/docs/HS_STATUS.md @@ -4,27 +4,27 @@ The optional ***homeSpan*** method, `void setStatusCallback(void (*func)(HS_STAT ```C++ enum HS_STATUS { - HS_WIFI_NEEDED, - HS_WIFI_CONNECTING, - HS_PAIRING_NEEDED, - HS_PAIRED, - HS_ENTERING_CONFIG_MODE, - HS_CONFIG_MODE_EXIT, - HS_CONFIG_MODE_REBOOT, - HS_CONFIG_MODE_LAUNCH_AP, - HS_CONFIG_MODE_UNPAIR, - HS_CONFIG_MODE_ERASE_WIFI, - HS_CONFIG_MODE_EXIT_SELECTED, - HS_CONFIG_MODE_REBOOT_SELECTED, - HS_CONFIG_MODE_LAUNCH_AP_SELECTED, - HS_CONFIG_MODE_UNPAIR_SELECTED, - HS_CONFIG_MODE_ERASE_WIFI_SELECTED, - HS_REBOOTING, - HS_FACTORY_RESET, - HS_AP_STARTED, - HS_AP_CONNECTED, - HS_AP_TERMINATED, - HS_OTA_STARTED + HS_WIFI_NEEDED, // WiFi Credentials have not yet been set/stored + HS_WIFI_CONNECTING, // HomeSpan is trying to connect to the network specified in the stored WiFi Credentials + HS_PAIRING_NEEDED, // HomeSpan is connected to central WiFi network, but device has not yet been paired to HomeKit + HS_PAIRED, // HomeSpan is connected to central WiFi network and ther device has been paired to HomeKit + HS_ENTERING_CONFIG_MODE, // User has requested the device to enter into Command Mode + HS_CONFIG_MODE_EXIT, // HomeSpan is in Command Mode with "Exit Command Mode" specified as choice + HS_CONFIG_MODE_REBOOT, // HomeSpan is in Command Mode with "Reboot" specified as choice + HS_CONFIG_MODE_LAUNCH_AP, // HomeSpan is in Command Mode with "Launch Access Point" specified as choice + HS_CONFIG_MODE_UNPAIR, // HomeSpan is in Command Mode with "Unpair Device" specified as choice + HS_CONFIG_MODE_ERASE_WIFI, // HomeSpan is in Command Mode with "Erase WiFi Credentials" specified as choice + HS_CONFIG_MODE_EXIT_SELECTED, // User has selected "Exit Command Mode" + HS_CONFIG_MODE_REBOOT_SELECTED, // User has select "Reboot" from the Command Mode + HS_CONFIG_MODE_LAUNCH_AP_SELECTED, // User has selected "Launch AP Access" from the Command Mode + HS_CONFIG_MODE_UNPAIR_SELECTED, // User has seleected "Unpair Device" from the Command Mode + HS_CONFIG_MODE_ERASE_WIFI_SELECTED, // User has selected "Erase WiFi Credentials" from the Command Mode + HS_REBOOTING, // HomeSpan is in the process of rebooting the device + HS_FACTORY_RESET, // HomeSpan is in the process of performing a Factory Reset of device + HS_AP_STARTED, // HomeSpan has started the Access Point but no one has yet connected + HS_AP_CONNECTED, // The Access Point is started and a user device has been connected + HS_AP_TERMINATED, // HomeSpan has terminated the Access Point + HS_OTA_STARTED // HomeSpan is in the process of recveived an Over-the-Air software update }; ``` From 5b1b9396a00a8d9e96179670f588849ce891949f Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Nov 2022 12:09:36 -0400 Subject: [PATCH 70/75] Update HomeSpan.h --- src/HomeSpan.h | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 5d38a6b..ec02347 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -70,27 +70,27 @@ enum { #define STATUS_UPDATE(LED_UPDATE,MESSAGE_UPDATE) {homeSpan.statusLED->LED_UPDATE;if(homeSpan.statusCallback)homeSpan.statusCallback(MESSAGE_UPDATE);} enum HS_STATUS { - HS_WIFI_NEEDED, - HS_WIFI_CONNECTING, - HS_PAIRING_NEEDED, - HS_PAIRED, - HS_ENTERING_CONFIG_MODE, - HS_CONFIG_MODE_EXIT, - HS_CONFIG_MODE_REBOOT, - HS_CONFIG_MODE_LAUNCH_AP, - HS_CONFIG_MODE_UNPAIR, - HS_CONFIG_MODE_ERASE_WIFI, - HS_CONFIG_MODE_EXIT_SELECTED, - HS_CONFIG_MODE_REBOOT_SELECTED, - HS_CONFIG_MODE_LAUNCH_AP_SELECTED, - HS_CONFIG_MODE_UNPAIR_SELECTED, - HS_CONFIG_MODE_ERASE_WIFI_SELECTED, - HS_REBOOTING, - HS_FACTORY_RESET, - HS_AP_STARTED, - HS_AP_CONNECTED, - HS_AP_TERMINATED, - HS_OTA_STARTED + HS_WIFI_NEEDED, // WiFi Credentials have not yet been set/stored + HS_WIFI_CONNECTING, // HomeSpan is trying to connect to the network specified in the stored WiFi Credentials + HS_PAIRING_NEEDED, // HomeSpan is connected to central WiFi network, but device has not yet been paired to HomeKit + HS_PAIRED, // HomeSpan is connected to central WiFi network and ther device has been paired to HomeKit + HS_ENTERING_CONFIG_MODE, // User has requested the device to enter into Command Mode + HS_CONFIG_MODE_EXIT, // HomeSpan is in Command Mode with "Exit Command Mode" specified as choice + HS_CONFIG_MODE_REBOOT, // HomeSpan is in Command Mode with "Reboot" specified as choice + HS_CONFIG_MODE_LAUNCH_AP, // HomeSpan is in Command Mode with "Launch Access Point" specified as choice + HS_CONFIG_MODE_UNPAIR, // HomeSpan is in Command Mode with "Unpair Device" specified as choice + HS_CONFIG_MODE_ERASE_WIFI, // HomeSpan is in Command Mode with "Erase WiFi Credentials" specified as choice + HS_CONFIG_MODE_EXIT_SELECTED, // User has selected "Exit Command Mode" + HS_CONFIG_MODE_REBOOT_SELECTED, // User has select "Reboot" from the Command Mode + HS_CONFIG_MODE_LAUNCH_AP_SELECTED, // User has selected "Launch AP Access" from the Command Mode + HS_CONFIG_MODE_UNPAIR_SELECTED, // User has seleected "Unpair Device" from the Command Mode + HS_CONFIG_MODE_ERASE_WIFI_SELECTED, // User has selected "Erase WiFi Credentials" from the Command Mode + HS_REBOOTING, // HomeSpan is in the process of rebooting the device + HS_FACTORY_RESET, // HomeSpan is in the process of performing a Factory Reset of device + HS_AP_STARTED, // HomeSpan has started the Access Point but no one has yet connected + HS_AP_CONNECTED, // The Access Point is started and a user device has been connected + HS_AP_TERMINATED, // HomeSpan has terminated the Access Point + HS_OTA_STARTED // HomeSpan is in the process of recveived an Over-the-Air software update }; /////////////////////////////// From 0aa84a80519114f2c921daa98441ddb469d2dffd Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 6 Nov 2022 13:44:40 -0500 Subject: [PATCH 71/75] Update README.md --- docs/README.md | 54 ++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/docs/README.md b/docs/README.md index 3fbd7cd..3186487 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ Welcome to HomeSpan - a robust and extremely easy-to-use Arduino library for cre HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit Accessory Protocol Specification Release R2 (HAP-R2)](https://developer.apple.com/homekit/specification/) designed specifically for the Espressif ESP32 microcontroller running within the Arduino IDE. HomeSpan pairs directly to HomeKit via your home WiFi network without the need for any external bridges or components. With HomeSpan you can use the full power of the ESP32's I/O functionality to create custom control software and/or hardware to automatically operate external devices from the Home App on your iPhone, iPad, or Mac, or with Siri. -HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](https://github.com/espressif/arduino-esp32), and has been tested up through version 2.0.4 (recommended). HomeSpan can be run on the original ESP32 as well as Espressif's ESP32-S2, ESP32-C3, and ESP32-S3 chips. +HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](https://github.com/espressif/arduino-esp32), and has been tested up through version 2.0.5 (recommended). HomeSpan can be run on the original ESP32 as well as Espressif's ESP32-S2, ESP32-C3, and ESP32-S3 chips. ### HomeSpan Highlights @@ -48,39 +48,29 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht * Launch the WiFi Access Point * A standalone, detailed End-User Guide -## ❗Latest Update - HomeSpan 1.6.0 (8/20/2022) +## ❗Latest Update - HomeSpan 1.7.0 (11/6/2022) -* **Support for ESP32-S3 devices** - * Requires Arduino-ESP32 Board Manager version 2.0.4 or later - -* **New functionality to *dynamically* add/delete Accessories on the fly without rebooting the device** - * Adds `homeSpan.deleteAccessory()` and `homeSpan.updateDatabase()` methods - * Includes new [Example 20 - AdvancedTechniques](https://github.com/HomeSpan/HomeSpan/blob/master/examples/20-AdvancedTechniques) to demonstrate how these methods can be used to create a dynamic bridge - * See [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) for details - -* **New support for Touch Pads** - * `SpanButton()` now supports three pin trigger methods: `TRIGGER_ON_LOW`, `TRIGGER_ON_HIGH`, and `TRIGGER_ON_TOUCH` - * Also allows users to add their own trigger methods so `SpanButton()` can monitor pushbuttons attached to pin extenders, pin multiplexers, or any other device that requires calling third-party library functions - * See [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) for details +* **ESP-NOW is now fully integrated into HomeSpan!** + * New dedicated class, **SpanPoint**, facilities robust device-to-device communication between multiple ESP32 devices + * Provides automatic calibration of WiFi channels to ensure compatibility with HomeSpan's normal WiFi connectivity + * Includes detailed [Example Sketches](Other%20Examples/RemoteSensors) demonstrating how SpanPoint can be used to implement a battery-powered Remote Sensor + * See the dedicated [SpanPoint Tutorial Page](NOW.md) for full details + +* **New functionality that allows a NeoPixel to be used as a Status LED** + * Adds`homeSpan.setStatusPixel()` method + * Works well with ESP32 boards that have a built-in NeoPixel LED, though adding an external NeoPixel is fine + * See the [API Reference](Reference.md) for details -* **Improved WiFi disconnect/reconnect handling** - * Fixes code that may, under certain circumstances, have prevented HomeSpan from reconnecting to WiFi after signal is lost - * Adds WiFi diagnostics to Web Logs to monitor for disconnects and WiFi signal strength - -* **New option to run HomeSpan as a separate task in its own thread** - * Adds `homeSpan.autoPoll()` method - * Works with both single- and dual-core processors - * For dual-core processors, polling task will be created on the "free" core not being used for other Arduino functions - * Allows user to add time-sensitive code the the main Arduino `loop()` function without delaying, or being dalyed by, HomeSpan polling - * See [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) for details - -* **New [Programmable Hub](https://github.com/HomeSpan/HomeSpan/blob/master/Other%20Examples/ProgrammableHub) Example** - * Demonstrates how to implement a Web Interface that allows users to dynamically add, delete, and configure up to 12 separate Light Accessories on a bridge device - * Expands upon many of the methods used in [Example 20](https://github.com/HomeSpan/HomeSpan/blob/master/examples/20-AdvancedTechniques) - -* **Additional updates include:** - * New 'm' CLI command that prints free bytes remaining in heap memory - useful for monitoring memory usage when dynamically adding Accessories - * New `homeSpan.getLogLevel()` method that returns the current log level +* **New functionality to track HomeSpan run-time status** + * Adds `homeSpan.setStatusCallback()` method providing users with a callback function whenever the internal state of HomeSpan changes, such as from *WiFi Needed* to *WiFi Connecting...* + * tracks changes to the run-time state of HomeSpan that would normally trigger a change in the blinking pattern of the (optional) Status LED + * See the [API Reference](Reference.md) for details + +* **Important Bug Fixes** + + * Fixed bug in controller-update logic associated with changes to the way Apple handles HomeKit Hubs that was producing *ERROR: Device not yet paired!* messages + + * Fixed bug in touch sensor logic that would cause compile failure when using Arduino-ESP32 versions 2.0.0-2.0.2 See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update. From 46c56284c3aace1aafb09b01ce5dfc86ed938df3 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 6 Nov 2022 13:51:37 -0500 Subject: [PATCH 72/75] Update README.md --- docs/README.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/README.md b/docs/README.md index 3186487..bf16c8e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -51,24 +51,24 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht ## ❗Latest Update - HomeSpan 1.7.0 (11/6/2022) * **ESP-NOW is now fully integrated into HomeSpan!** - * New dedicated class, **SpanPoint**, facilities robust device-to-device communication between multiple ESP32 devices + * New dedicated class, **SpanPoint**, that facilitates bi-directional device-to-device communication between multiple ESP32 devices * Provides automatic calibration of WiFi channels to ensure compatibility with HomeSpan's normal WiFi connectivity - * Includes detailed [Example Sketches](Other%20Examples/RemoteSensors) demonstrating how SpanPoint can be used to implement a battery-powered Remote Sensor + * Includes detailed [Example Sketches](../Other%20Examples/RemoteSensors) demonstrating how SpanPoint can be used to implement a battery-powered Remote Sensor * See the dedicated [SpanPoint Tutorial Page](NOW.md) for full details -* **New functionality that allows a NeoPixel to be used as a Status LED** +* **NeoPixels can now be used as a Status LED** * Adds`homeSpan.setStatusPixel()` method * Works well with ESP32 boards that have a built-in NeoPixel LED, though adding an external NeoPixel is fine * See the [API Reference](Reference.md) for details * **New functionality to track HomeSpan run-time status** * Adds `homeSpan.setStatusCallback()` method providing users with a callback function whenever the internal state of HomeSpan changes, such as from *WiFi Needed* to *WiFi Connecting...* - * tracks changes to the run-time state of HomeSpan that would normally trigger a change in the blinking pattern of the (optional) Status LED + * Tracks changes to the run-time state of HomeSpan that would normally trigger a change in the blinking pattern of the (optional) Status LED * See the [API Reference](Reference.md) for details * **Important Bug Fixes** - * Fixed bug in controller-update logic associated with changes to the way Apple handles HomeKit Hubs that was producing *ERROR: Device not yet paired!* messages + * Fixed bug in controller-update logic associated with changes to the way Apple now handles HomeKit Hubs that was producing *ERROR: Device not yet paired!* messages * Fixed bug in touch sensor logic that would cause compile failure when using Arduino-ESP32 versions 2.0.0-2.0.2 @@ -78,24 +78,24 @@ See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all HomeSpan includes the following documentation: -* [Getting Started with HomeSpan](https://github.com/HomeSpan/HomeSpan/blob/master/docs/GettingStarted.md) - setting up the software and the hardware needed to develop HomeSpan devices -* [HomeSpan API Overview](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Overview.md) - an overview of the HomeSpan API, including a step-by-step guide to developing your first HomeSpan Sketch -* [HomeSpan Tutorials](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Tutorials.md) - a guide to HomeSpan's tutorial-sketches -* [HomeSpan Services and Characteristics](https://github.com/HomeSpan/HomeSpan/blob/master/docs/ServiceList.md) - a list of all HAP Services and Characterstics supported by HomeSpan -* [HomeSpan Accessory Categories](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Categories.md) - a list of all HAP Accessory Categories defined by HomeSpan -* [HomeSpan Command-Line Interface (CLI)](https://github.com/HomeSpan/HomeSpan/blob/master/docs/CLI.md) - configure a HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, monitor and update its status, and access detailed, real-time device diagnostics from the Arduino IDE Serial Monitor -* [HomeSpan User Guide](https://github.com/HomeSpan/HomeSpan/blob/master/docs/UserGuide.md) - turnkey instructions on how to configure an already-programmed HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, and pair the device to HomeKit. No computer needed! -* [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) - a complete guide to the HomeSpan Library API -* [HomeSpan QR Codes](https://github.com/HomeSpan/HomeSpan/blob/master/docs/QRCodes.md) - create and use QR Codes for pairing HomeSpan devices -* [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection -* [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral -* [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral -* [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips -* [HomeSpan SpanPoint](https://github.com/HomeSpan/HomeSpan/blob/master/docs/NOW.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW -* [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics -* [HomeSpan Message Logging](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page +* [Getting Started with HomeSpan](GettingStarted.md) - setting up the software and the hardware needed to develop HomeSpan devices +* [HomeSpan API Overview](Overview.md) - an overview of the HomeSpan API, including a step-by-step guide to developing your first HomeSpan Sketch +* [HomeSpan Tutorials](Tutorials.md) - a guide to HomeSpan's tutorial-sketches +* [HomeSpan Services and Characteristics](ServiceList.md) - a list of all HAP Services and Characterstics supported by HomeSpan +* [HomeSpan Accessory Categories](Categories.md) - a list of all HAP Accessory Categories defined by HomeSpan +* [HomeSpan Command-Line Interface (CLI)](CLI.md) - configure a HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, monitor and update its status, and access detailed, real-time device diagnostics from the Arduino IDE Serial Monitor +* [HomeSpan User Guide](UserGuide.md) - turnkey instructions on how to configure an already-programmed HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, and pair the device to HomeKit. No computer needed! +* [HomeSpan API Reference](Reference.md) - a complete guide to the HomeSpan Library API +* [HomeSpan QR Codes](QRCodes.md) - create and use QR Codes for pairing HomeSpan devices +* [HomeSpan OTA](OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection +* [HomeSpan PWM](PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral +* [HomeSpan RFControl](RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral +* [HomeSpan Pixels](Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips +* [HomeSpan SpanPoint](NOW.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW +* [HomeSpan Television Services](TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics +* [HomeSpan Message Logging](Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page * [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library -* [HomeSpan FAQ](https://github.com/HomeSpan/HomeSpan/blob/master/docs/FAQ.md) - answers to frequently-asked questions +* [HomeSpan FAQ](FAQ.md) - answers to frequently-asked questions Note that all documentation is version-controlled and tied to each branch. The *master* branch generally points to the latest release. The *dev* branch, when available, will contain code under active development. From 8ff9ebc2c1c67cffba4c9609719ff6a39c597936 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 6 Nov 2022 13:58:01 -0500 Subject: [PATCH 73/75] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index bf16c8e..fe12d5b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -58,7 +58,7 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht * **NeoPixels can now be used as a Status LED** * Adds`homeSpan.setStatusPixel()` method - * Works well with ESP32 boards that have a built-in NeoPixel LED, though adding an external NeoPixel is fine + * Works well with ESP32 boards that have a built-in NeoPixel LED * See the [API Reference](Reference.md) for details * **New functionality to track HomeSpan run-time status** From a761d992e8720797776c1bb47a42cdebe0a6175f Mon Sep 17 00:00:00 2001 From: Gregg Date: Tue, 8 Nov 2022 05:37:25 -0600 Subject: [PATCH 74/75] Updated version number to 1.7.0 --- src/Settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Settings.h b/src/Settings.h index 7ef9de3..40d6bd8 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -35,7 +35,7 @@ // HomeSpan Version // #define HS_MAJOR 1 -#define HS_MINOR 6 +#define HS_MINOR 7 #define HS_PATCH 0 #define STRINGIFY(x) _STR(x) From 1c5ece051224cdc8e9f84dfc7cdea1b250b9d4d5 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Tue, 8 Nov 2022 05:44:47 -0600 Subject: [PATCH 75/75] Update library.properties --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 0a523e6..9dc7fbd 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HomeSpan -version=1.6.0 +version=1.7.0 author=Gregg maintainer=Gregg sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE.