Compare commits

...

18 commits
v2.4.3 ... main

Author SHA1 Message Date
Robert Strouse
eb75868adb Update build params for C3 #458 #454 2024-08-19 10:46:00 -07:00
Robert Strouse
3d9e6c11c2 Clear the connecting flag for ETH Wifi fallback #445 2024-08-04 09:32:18 -07:00
Robert Strouse
e478d17c7f Bypass AP Scanning with Use Hidden SSID option #438 #434 2024-07-27 12:12:38 -07:00
Robert Strouse
93ebddd29d Prepare v2.4.6 Release 2024-07-25 17:14:29 -07:00
Robert Strouse
fb1f18e260 Ensure the network connection has completed before trying MQTT. 2024-07-14 09:59:14 -07:00
Robert Strouse
c3ada3b40e removed pin restrictions for S3 #426 2024-07-13 07:18:26 -07:00
Robert Strouse
75928b4ac8 Remove autoReconnect function from core #410 #407 2024-07-10 21:40:55 -07:00
Robert Strouse
0b985c0880 More aggressive WIFI scanning #410 #407 2024-06-28 14:37:41 -07:00
Robert Strouse
e04d7e3fc7 Include scan with SoftAP operation #407 #410 2024-06-25 18:04:46 -07:00
Robert Strouse
9205723125 Reduce heap emits for interface sockets 2024-06-19 11:21:51 -07:00
Robert Strouse
2b59f330a9 Add support for 1 button gates #388 2024-06-18 14:05:43 -07:00
Robert Strouse
c528fda55a Fix issue with deleting repeaters #402 2024-06-17 08:47:44 -07:00
Robert Strouse
f29cd9c089 Add additional command structure for 80-bit up/down 2024-06-15 09:22:27 -07:00
Robert Strouse
cf7a9b1fc2 Fixes for TaHoma hub #393 2024-06-07 19:06:27 -07:00
Robert Strouse
473307b320 Added additional checks for wifi dropoffs and disconnect from STA for AP scan. 2024-06-02 10:58:51 -07:00
Robert Strouse
2dc49a64e9 Bump application to v2.4.4 2024-05-26 16:36:29 -07:00
Robert Strouse
5f15a7cc93 Bump workflow actions 2024-05-26 16:05:04 -07:00
Robert Strouse
e87f42fa50 Update reconnect procedure #385 2024-05-26 15:59:18 -07:00
26 changed files with 701 additions and 422 deletions

View file

@ -7,7 +7,7 @@ on:
env: env:
ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json" ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json"
ARDUINO_CLI_VERSION: "0.x" ARDUINO_CLI_VERSION: "0.x"
ARDUINO_ESP32_VERSION: "2.0.16" ARDUINO_ESP32_VERSION: "2.0.17"
ARDUINO_JSON_VERSION: "6.21.5" ARDUINO_JSON_VERSION: "6.21.5"
ESPTOOL_VERSION: "4.7" ESPTOOL_VERSION: "4.7"
LITTLEFS_VERSION: "v2.5.1" LITTLEFS_VERSION: "v2.5.1"
@ -30,17 +30,17 @@ jobs:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
- name: Check out code - name: Check out code
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Checkout mklittlefs - name: Checkout mklittlefs
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
repository: earlephilhower/mklittlefs repository: earlephilhower/mklittlefs
path: mklittlefs path: mklittlefs
ref: ${{ env.MKLITTLEFS_VERSION }} ref: ${{ env.MKLITTLEFS_VERSION }}
- name: Checkout LittleFS - name: Checkout LittleFS
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
repository: littlefs-project/littlefs repository: littlefs-project/littlefs
path: mklittlefs/littlefs path: mklittlefs/littlefs
@ -55,14 +55,14 @@ jobs:
./mklittlefs/mklittlefs --create data --size 1441792 SomfyController.littlefs.bin ./mklittlefs/mklittlefs --create data --size 1441792 SomfyController.littlefs.bin
- name: Upload binaries - name: Upload binaries
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: LittleFS name: LittleFS
path: SomfyController.littlefs.bin path: SomfyController.littlefs.bin
retention-days: 5 retention-days: 5
- name: Upload LittleFS - name: Upload LittleFS
uses: shogo82148/actions-upload-release-asset@v1.7.2 uses: shogo82148/actions-upload-release-asset@v1.7.5
with: with:
github_token: ${{ github.token }} github_token: ${{ github.token }}
upload_url: ${{ steps.get_release.outputs.upload_url }} upload_url: ${{ steps.get_release.outputs.upload_url }}
@ -91,7 +91,7 @@ jobs:
- board: esp32c3 - board: esp32c3
addr_bootloader: 0x0 addr_bootloader: 0x0
chip: ESP32-C3 chip: ESP32-C3
fqbn: esp32:esp32:esp32c3:JTAGAdapter=default,CDCOnBoot=default,PartitionScheme=default,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none fqbn: esp32:esp32:esp32c3:JTAGAdapter=default,CDCOnBoot=cdc,PartitionScheme=default,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
# esp32:esp32:esp32c3:JTAGAdapter=default,CDCOnBoot=default,PartitionScheme=default,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none # esp32:esp32:esp32c3:JTAGAdapter=default,CDCOnBoot=default,PartitionScheme=default,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
name: ESP32C3 name: ESP32C3
obname: SomfyController.onboard.esp32c3.bin obname: SomfyController.onboard.esp32c3.bin
@ -99,7 +99,7 @@ jobs:
- board: esp32s2 - board: esp32s2
addr_bootloader: 0x1000 addr_bootloader: 0x1000
chip: ESP32-S2 chip: ESP32-S2
fqbn: esp32:esp32:esp32s2:JTAGAdapter=default,CDCOnBoot=default,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none fqbn: esp32:esp32:esp32s2:JTAGAdapter=default,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
# esp32:esp32:esp32s2:JTAGAdapter=default,CDCOnBoot=default,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none # esp32:esp32:esp32s2:JTAGAdapter=default,CDCOnBoot=default,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
name: ESP32S2 name: ESP32S2
obname: SomfyController.onboard.esp32s2.bin obname: SomfyController.onboard.esp32s2.bin
@ -120,17 +120,17 @@ jobs:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
- name: Check out code - name: Check out code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
path: SomfyController path: SomfyController
- name: Get LittleFS - name: Get LittleFS
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: LittleFS name: LittleFS
- name: Install Python ${{ env.PYTHON_VERSION }} - name: Install Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
@ -175,7 +175,7 @@ jobs:
0x290000 SomfyController.littlefs.bin 0x290000 SomfyController.littlefs.bin
- name: Upload Firmware ${{ matrix.name }} - name: Upload Firmware ${{ matrix.name }}
uses: shogo82148/actions-upload-release-asset@v1.7.2 uses: shogo82148/actions-upload-release-asset@v1.7.5
with: with:
github_token: ${{ github.token }} github_token: ${{ github.token }}
upload_url: ${{ steps.get_release.outputs.upload_url }} upload_url: ${{ steps.get_release.outputs.upload_url }}
@ -187,7 +187,7 @@ jobs:
zip ${{ matrix.obname }}.zip ./${{ matrix.obname }} zip ${{ matrix.obname }}.zip ./${{ matrix.obname }}
- name: Upload Onboard ${{ matrix.name }} - name: Upload Onboard ${{ matrix.name }}
uses: shogo82148/actions-upload-release-asset@v1.7.2 uses: shogo82148/actions-upload-release-asset@v1.7.5
# env: # env:
# GITHUB_TOKEN: ${{ github.token }} # GITHUB_TOKEN: ${{ github.token }}
with: with:

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ debug.cfg
SomfyController.ino.XIAO_ESP32S3.bin SomfyController.ino.XIAO_ESP32S3.bin
SomfyController.ino.esp32c3.bin SomfyController.ino.esp32c3.bin
SomfyController.ino.esp32s2.bin SomfyController.ino.esp32s2.bin
.vscode/settings.json

View file

@ -614,7 +614,7 @@ bool ShadeConfigFile::readNetRecord(restore_options_t &opts) {
uint32_t startPos = this->file.position(); uint32_t startPos = this->file.position();
if(opts.network) { if(opts.network) {
Serial.println("Reading network settings from file..."); Serial.println("Reading network settings from file...");
settings.connType = static_cast<conn_types>(this->readUInt8(static_cast<uint8_t>(conn_types::unset))); settings.connType = static_cast<conn_types_t>(this->readUInt8(static_cast<uint8_t>(conn_types_t::unset)));
settings.IP.dhcp = this->readBool(true); settings.IP.dhcp = this->readBool(true);
char ip[24]; char ip[24];
this->readVarString(ip, sizeof(ip)); this->readVarString(ip, sizeof(ip));

View file

@ -205,12 +205,12 @@ bool ConfigSettings::load() {
pref.getString("hostname", this->hostname, sizeof(this->hostname)); pref.getString("hostname", this->hostname, sizeof(this->hostname));
this->ssdpBroadcast = pref.getBool("ssdpBroadcast", true); this->ssdpBroadcast = pref.getBool("ssdpBroadcast", true);
this->checkForUpdate = pref.getBool("checkForUpdate", true); this->checkForUpdate = pref.getBool("checkForUpdate", true);
this->connType = static_cast<conn_types>(pref.getChar("connType", 0x00)); this->connType = static_cast<conn_types_t>(pref.getChar("connType", 0x00));
//Serial.printf("Preference GFG Free Entries: %d\n", pref.freeEntries()); //Serial.printf("Preference GFG Free Entries: %d\n", pref.freeEntries());
pref.end(); pref.end();
if(this->connType == conn_types::unset) { if(this->connType == conn_types_t::unset) {
// We are doing this to convert the data from previous versions. // We are doing this to convert the data from previous versions.
this->connType = conn_types::wifi; this->connType = conn_types_t::wifi;
pref.begin("WIFI"); pref.begin("WIFI");
pref.getString("hostname", this->hostname, sizeof(this->hostname)); pref.getString("hostname", this->hostname, sizeof(this->hostname));
this->ssdpBroadcast = pref.getBool("ssdpBroadcast", true); this->ssdpBroadcast = pref.getBool("ssdpBroadcast", true);
@ -261,7 +261,7 @@ bool ConfigSettings::requiresAuth() { return this->Security.type != security_typ
bool ConfigSettings::fromJSON(JsonObject &obj) { bool ConfigSettings::fromJSON(JsonObject &obj) {
if(obj.containsKey("ssdpBroadcast")) this->ssdpBroadcast = obj["ssdpBroadcast"]; if(obj.containsKey("ssdpBroadcast")) this->ssdpBroadcast = obj["ssdpBroadcast"];
if(obj.containsKey("hostname")) this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname)); if(obj.containsKey("hostname")) this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname));
if(obj.containsKey("connType")) this->connType = static_cast<conn_types>(obj["connType"].as<uint8_t>()); if(obj.containsKey("connType")) this->connType = static_cast<conn_types_t>(obj["connType"].as<uint8_t>());
if(obj.containsKey("checkForUpdate")) this->checkForUpdate = obj["checkForUpdate"]; if(obj.containsKey("checkForUpdate")) this->checkForUpdate = obj["checkForUpdate"];
return true; return true;
} }
@ -269,8 +269,8 @@ void ConfigSettings::print() {
this->Security.print(); this->Security.print();
Serial.printf("Connection Type: %u\n", (unsigned int) this->connType); Serial.printf("Connection Type: %u\n", (unsigned int) this->connType);
this->NTP.print(); this->NTP.print();
if(this->connType == conn_types::wifi || this->connType == conn_types::unset) this->WIFI.print(); if(this->connType == conn_types_t::wifi || this->connType == conn_types_t::unset) this->WIFI.print();
if(this->connType == conn_types::ethernet || this->connType == conn_types::ethernetpref) this->Ethernet.print(); if(this->connType == conn_types_t::ethernet || this->connType == conn_types_t::ethernetpref) this->Ethernet.print();
} }
void ConfigSettings::emitSockets() {} void ConfigSettings::emitSockets() {}
void ConfigSettings::emitSockets(uint8_t num) {} void ConfigSettings::emitSockets(uint8_t num) {}
@ -580,18 +580,21 @@ bool WifiSettings::fromJSON(JsonObject &obj) {
this->parseValueString(obj, "ssid", this->ssid, sizeof(this->ssid)); this->parseValueString(obj, "ssid", this->ssid, sizeof(this->ssid));
this->parseValueString(obj, "passphrase", this->passphrase, sizeof(this->passphrase)); this->parseValueString(obj, "passphrase", this->passphrase, sizeof(this->passphrase));
if(obj.containsKey("roaming")) this->roaming = obj["roaming"]; if(obj.containsKey("roaming")) this->roaming = obj["roaming"];
if(obj.containsKey("hidden")) this->hidden = obj["hidden"];
return true; return true;
} }
bool WifiSettings::toJSON(JsonObject &obj) { bool WifiSettings::toJSON(JsonObject &obj) {
obj["ssid"] = this->ssid; obj["ssid"] = this->ssid;
obj["passphrase"] = this->passphrase; obj["passphrase"] = this->passphrase;
obj["roaming"] = this->roaming; obj["roaming"] = this->roaming;
obj["hidden"] = this->hidden;
return true; return true;
} }
void WifiSettings::toJSON(JsonResponse &json) { void WifiSettings::toJSON(JsonResponse &json) {
json.addElem("ssid", this->ssid); json.addElem("ssid", this->ssid);
json.addElem("passphrase", this->passphrase); json.addElem("passphrase", this->passphrase);
json.addElem("roaming", this->roaming); json.addElem("roaming", this->roaming);
json.addElem("hidden", this->hidden);
} }
bool WifiSettings::save() { bool WifiSettings::save() {
@ -600,6 +603,7 @@ bool WifiSettings::save() {
pref.putString("ssid", this->ssid); pref.putString("ssid", this->ssid);
pref.putString("passphrase", this->passphrase); pref.putString("passphrase", this->passphrase);
pref.putBool("roaming", this->roaming); pref.putBool("roaming", this->roaming);
pref.putBool("hidden", this->hidden);
pref.end(); pref.end();
return true; return true;
} }
@ -610,6 +614,7 @@ bool WifiSettings::load() {
this->ssid[sizeof(this->ssid) - 1] = '\0'; this->ssid[sizeof(this->ssid) - 1] = '\0';
this->passphrase[sizeof(this->passphrase) - 1] = '\0'; this->passphrase[sizeof(this->passphrase) - 1] = '\0';
this->roaming = pref.getBool("roaming", true); this->roaming = pref.getBool("roaming", true);
this->hidden = pref.getBool("hidden", false);
pref.end(); pref.end();
return true; return true;
} }

View file

@ -3,7 +3,15 @@
#ifndef configsettings_h #ifndef configsettings_h
#define configsettings_h #define configsettings_h
#include "WResp.h" #include "WResp.h"
#define FW_VERSION "v2.4.3" #define FW_VERSION "v2.4.7"
enum class conn_types_t : byte {
unset = 0x00,
wifi = 0x01,
ethernet = 0x02,
ethernetpref = 0x03,
ap = 0x04
};
enum DeviceStatus { enum DeviceStatus {
DS_OK = 0, DS_OK = 0,
DS_ERROR = 1, DS_ERROR = 1,
@ -64,6 +72,7 @@ class WifiSettings: BaseSettings {
public: public:
WifiSettings(); WifiSettings();
bool roaming = true; bool roaming = true;
bool hidden = false;
char ssid[65] = ""; char ssid[65] = "";
char passphrase[65] = ""; char passphrase[65] = "";
//bool ssdpBroadcast = true; //bool ssdpBroadcast = true;
@ -157,20 +166,13 @@ class MQTTSettings: BaseSettings {
void toJSON(JsonResponse &json); void toJSON(JsonResponse &json);
bool fromJSON(JsonObject &obj); bool fromJSON(JsonObject &obj);
}; };
enum class conn_types : byte {
unset = 0x00,
wifi = 0x01,
ethernet = 0x02,
ethernetpref = 0x03,
ap = 0x04
};
class ConfigSettings: BaseSettings { class ConfigSettings: BaseSettings {
public: public:
static void printAvailHeap(); static void printAvailHeap();
char serverId[10] = ""; char serverId[10] = "";
char hostname[32] = "ESPSomfyRTS"; char hostname[32] = "ESPSomfyRTS";
char chipModel[10] = "ESP32"; char chipModel[10] = "ESP32";
conn_types connType = conn_types::unset; conn_types_t connType = conn_types_t::unset;
appver_t fwVersion; appver_t fwVersion;
appver_t appVersion; appver_t appVersion;
bool ssdpBroadcast = true; bool ssdpBroadcast = true;
@ -197,5 +199,4 @@ class ConfigSettings: BaseSettings {
uint16_t calcNetRecSize(); uint16_t calcNetRecSize();
bool getAppVersion(); bool getAppVersion();
}; };
#endif #endif

View file

@ -3,13 +3,15 @@
#include <Update.h> #include <Update.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include <esp_task_wdt.h> #include <esp_task_wdt.h>
#include "ConfigSettings.h"
#include "GitOTA.h" #include "GitOTA.h"
#include "Utils.h" #include "Utils.h"
#include "ConfigSettings.h"
#include "Sockets.h" #include "Sockets.h"
#include "Somfy.h" #include "Somfy.h"
#include "Web.h" #include "Web.h"
#include "WResp.h" #include "WResp.h"
#include "Network.h"
@ -18,6 +20,7 @@ extern SocketEmitter sockEmit;
extern SomfyShadeController somfy; extern SomfyShadeController somfy;
extern rebootDelay_t rebootDelay; extern rebootDelay_t rebootDelay;
extern Web webServer; extern Web webServer;
extern Network net;
@ -249,9 +252,10 @@ void GitRepo::toJSON(JsonResponse &json) {
#define ERR_DOWNLOAD_CONNECTION -42 #define ERR_DOWNLOAD_CONNECTION -42
void GitUpdater::loop() { void GitUpdater::loop() {
if(!net.connected()) return;
if(this->status == GIT_STATUS_READY) { if(this->status == GIT_STATUS_READY) {
if(settings.checkForUpdate && if(settings.checkForUpdate &&
(millis() > 60000) && // Wait a minute before checking after boot. (millis() > net.connectTime + 60000) && // Wait a minute before checking after connection.
(this->lastCheck + 86400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 1 day (this->lastCheck + 86400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 1 day
this->checkForUpdate(); this->checkForUpdate();
} }
@ -356,7 +360,9 @@ int GitUpdater::checkInternet() {
if(https.begin(sclient, "https://github.com/rstrouse/ESPSomfy-RTS")) { if(https.begin(sclient, "https://github.com/rstrouse/ESPSomfy-RTS")) {
https.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); https.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
https.setTimeout(3000); https.setTimeout(3000);
esp_task_wdt_reset();
int httpCode = https.sendRequest("HEAD"); int httpCode = https.sendRequest("HEAD");
esp_task_wdt_reset();
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY || httpCode == HTTP_CODE_FOUND) { if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY || httpCode == HTTP_CODE_FOUND) {
err = 0; err = 0;
Serial.printf("Internet is Available: %ldms\n", millis() - t); Serial.printf("Internet is Available: %ldms\n", millis() - t);

View file

@ -1,8 +1,9 @@
#include <WiFi.h> #include <WiFi.h>
#include <PubSubClient.h> #include <PubSubClient.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "MQTT.h" #include <esp_task_wdt.h>
#include "ConfigSettings.h" #include "ConfigSettings.h"
#include "MQTT.h"
#include "Somfy.h" #include "Somfy.h"
#include "Network.h" #include "Network.h"
#include "Utils.h" #include "Utils.h"
@ -34,12 +35,16 @@ void MQTTClass::reset() {
this->connect(); this->connect();
} }
bool MQTTClass::loop() { bool MQTTClass::loop() {
if(settings.MQTT.enabled && !rebootDelay.reboot && !this->suspended && !mqttClient.connected()) if(settings.MQTT.enabled && !rebootDelay.reboot && !this->suspended && !mqttClient.connected()) {
this->connect(); esp_task_wdt_reset();
if(!this->connected() && net.connected()) this->connect();
}
esp_task_wdt_reset();
if(settings.MQTT.enabled) mqttClient.loop(); if(settings.MQTT.enabled) mqttClient.loop();
return true; return true;
} }
void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) { void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
esp_task_wdt_reset(); // Make sure we do not reboot here.
Serial.print("MQTT Topic:"); Serial.print("MQTT Topic:");
Serial.print(topic); Serial.print(topic);
Serial.print(" payload:"); Serial.print(" payload:");
@ -178,8 +183,10 @@ void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
} }
} }
} }
esp_task_wdt_reset(); // Make sure we do not reboot here.
} }
bool MQTTClass::connect() { bool MQTTClass::connect() {
esp_task_wdt_reset(); // Make sure we do not reboot here.
if(mqttClient.connected()) { if(mqttClient.connected()) {
if(!settings.MQTT.enabled || this->suspended) if(!settings.MQTT.enabled || this->suspended)
return this->disconnect(); return this->disconnect();
@ -195,6 +202,7 @@ bool MQTTClass::connect() {
char lwtTopic[128] = "status"; char lwtTopic[128] = "status";
if(strlen(settings.MQTT.rootTopic) > 0) if(strlen(settings.MQTT.rootTopic) > 0)
snprintf(lwtTopic, sizeof(lwtTopic), "%s/status", settings.MQTT.rootTopic); snprintf(lwtTopic, sizeof(lwtTopic), "%s/status", settings.MQTT.rootTopic);
esp_task_wdt_reset();
if(mqttClient.connect(this->clientId, settings.MQTT.username, settings.MQTT.password, lwtTopic, 0, true, "offline")) { if(mqttClient.connect(this->clientId, settings.MQTT.username, settings.MQTT.password, lwtTopic, 0, true, "offline")) {
Serial.print("Successfully connected MQTT client "); Serial.print("Successfully connected MQTT client ");
Serial.println(this->clientId); Serial.println(this->clientId);
@ -219,8 +227,9 @@ bool MQTTClass::connect() {
this->subscribe("groups/+/sunFlag/set"); this->subscribe("groups/+/sunFlag/set");
this->subscribe("groups/+/sunny/set"); this->subscribe("groups/+/sunny/set");
this->subscribe("groups/+/windy/set"); this->subscribe("groups/+/windy/set");
mqttClient.setCallback(MQTTClass::receive); mqttClient.setCallback(MQTTClass::receive);
Serial.println("MQTT Startup Completed");
esp_task_wdt_reset();
this->lastConnect = millis(); this->lastConnect = millis();
return true; return true;
} }
@ -272,6 +281,7 @@ bool MQTTClass::unsubscribe(const char *topic) {
} }
bool MQTTClass::subscribe(const char *topic) { bool MQTTClass::subscribe(const char *topic) {
if(mqttClient.connected()) { if(mqttClient.connected()) {
esp_task_wdt_reset(); // Make sure we do not reboot here.
char top[128]; char top[128];
if(strlen(settings.MQTT.rootTopic) > 0) if(strlen(settings.MQTT.rootTopic) > 0)
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic); snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
@ -290,6 +300,7 @@ bool MQTTClass::publish(const char *topic, const char *payload, bool retain) {
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic); snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
else else
strlcpy(top, topic, sizeof(top)); strlcpy(top, topic, sizeof(top));
esp_task_wdt_reset(); // Make sure we do not reboot here.
mqttClient.publish(top, payload, retain); mqttClient.publish(top, payload, retain);
return true; return true;
} }
@ -306,6 +317,7 @@ bool MQTTClass::unpublish(const char *topic) {
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic); snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
else else
strlcpy(top, topic, sizeof(top)); strlcpy(top, topic, sizeof(top));
esp_task_wdt_reset(); // Make sure we do not reboot here.
mqttClient.publish(top, (const uint8_t *)"", 0, true); mqttClient.publish(top, (const uint8_t *)"", 0, true);
return true; return true;
} }
@ -320,6 +332,7 @@ bool MQTTClass::publishBuffer(const char *topic, uint8_t *data, uint16_t len, bo
uint16_t offset = 0; uint16_t offset = 0;
uint16_t to_write = len; uint16_t to_write = len;
uint16_t buff_len; uint16_t buff_len;
esp_task_wdt_reset(); // Make sure we do not reboot here.
mqttClient.beginPublish(topic, len, retain); mqttClient.beginPublish(topic, len, retain);
do { do {
buff_len = to_write; buff_len = to_write;

View file

@ -18,6 +18,7 @@ extern rebootDelay_t rebootDelay;
extern Network net; extern Network net;
extern SomfyShadeController somfy; extern SomfyShadeController somfy;
static unsigned long _lastHeapEmit = 0;
static bool _apScanning = false; static bool _apScanning = false;
static uint32_t _lastMaxHeap = 0; static uint32_t _lastMaxHeap = 0;
@ -33,125 +34,150 @@ bool Network::setup() {
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
WiFi.persistent(false); WiFi.persistent(false);
WiFi.setAutoReconnect(false);
WiFi.onEvent(this->networkEvent); WiFi.onEvent(this->networkEvent);
this->disconnectTime = millis();
if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true, true); if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true, true);
if(settings.connType == conn_types::wifi || settings.connType == conn_types::unset) { if(settings.connType == conn_types_t::wifi || settings.connType == conn_types_t::unset) {
WiFi.persistent(false); WiFi.persistent(false);
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname); if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
Serial.print("WiFi Mode: "); Serial.print("WiFi Mode: ");
Serial.println(WiFi.getMode()); Serial.println(WiFi.getMode());
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
settings.WIFI.printNetworks();
} }
//if(!this->connect()) this->openSoftAP(); sockEmit.begin();
return true; return true;
} }
conn_types_t Network::preferredConnType() {
switch(settings.connType) {
case conn_types_t::wifi:
return settings.WIFI.ssid[0] != '\0' ? conn_types_t::wifi : conn_types_t::ap;
case conn_types_t::unset:
case conn_types_t::ap:
return conn_types_t::ap;
case conn_types_t::ethernetpref:
return settings.WIFI.ssid[0] != '\0' && (!ETH.linkUp() && this->ethStarted) ? conn_types_t::wifi : conn_types_t::ethernet;
case conn_types_t::ethernet:
return ETH.linkUp() || !this->ethStarted ? conn_types_t::ethernet : conn_types_t::ap;
default:
return settings.connType;
}
}
void Network::loop() { void Network::loop() {
this->connect(); // ORDER OF OPERATIONS:
if(!this->connected() || this->connecting()) return; // ----------------------------------------------
if(millis() - this->lastEmit > 1500) { // 1. If we are in the middle of a connection process we need to simply bail after the connect method. The
this->lastEmit = millis(); // connect method will take care of our target connection for us.
if(!this->softAPOpened) { // 2. Check to see what type of target connection we need.
if(this->connected()) { // a. If this is an ethernet target then the connection needs to perform a fallback if applicable.
this->emitSockets(); // b. If this is a wifi target then we need to first check to see if the SSID is available.
this->lastEmit = millis(); // c. If an SSID has not been set then we need to turn on the Soft AP.
} // 3. If the Soft AP is open and the target is either wifi, ethernet, or ethernetpref then
} // we need to shut it down if there are no connections and the preferred connection is available.
} // a. Ethernet: Check for an active ethernet connection. We cannot rely on linkup because the PHY will
sockEmit.loop(); // report that the link is up when no IP address is being served.
if(this->connected() && millis() - this->lastMDNS > 60000) { // b. WiFi: Perform synchronous scan for APs related to the SSID. If the SSID can be found then perform
// Every 60 seconds we are going to look at wifi connectivity // the connection process for the WiFi connection.
// to get around the roaming issues with ESP32. We will try to do this in an async manner. If // c. SoftAP: This condition retains the Soft AP because no other connection method is available.
// there is a channel that is better we will stop the wifi radio and reconnect conn_types_t ctype = this->preferredConnType();
if(this->connType == conn_types::wifi && settings.WIFI.roaming && !this->softAPOpened) { this->connect(ctype); // Connection timeout handled in connect function as well as the opening of the Soft AP if needed.
// If we are not already scanning then we need to start a passive scan if(this->connecting()) return; // If we are currently attempting to connect to something then we need to bail here.
// and only respond if there is a better connection.
// 1. If there is currently a waiting scan don't do anything
if(!_apScanning && WiFi.scanNetworks(true, false, true, 300, 0, settings.WIFI.ssid) == -1) {
_apScanning = true;
}
}
this->lastMDNS = millis();
}
if(_apScanning) { if(_apScanning) {
if(!settings.WIFI.roaming || this->connType != conn_types::wifi || this->softAPOpened) _apScanning = false; if(settings.WIFI.hidden || // This user has elected to use a hidden AP.
(this->connected() && !settings.WIFI.roaming) || // We are already connected and should not be roaming.
(this->softAPOpened && WiFi.softAPgetStationNum() != 0) || // The Soft AP is open and a user is connected.
(ctype != conn_types_t::wifi)) { // The Ethernet link is up so we should ignore this scan.
Serial.println("Cancelling WiFi STA Scan...");
_apScanning = false;
WiFi.scanDelete();
}
else { else {
uint16_t n = WiFi.scanComplete(); int16_t n = WiFi.scanComplete();
if( n > 0) { if( n >= 0) { // If the scan is complete but the WiFi isn't ready this can return 0.
uint8_t bssid[6]; uint8_t bssid[6];
int32_t channel = 0; int32_t channel = 0;
if(this->getStrongestAP(settings.WIFI.ssid, bssid, &channel)) { if(this->getStrongestAP(settings.WIFI.ssid, bssid, &channel)) {
if(memcmp(bssid, WiFi.BSSID(), sizeof(bssid)) != 0) { if(!WiFi.BSSID() || memcmp(bssid, WiFi.BSSID(), sizeof(bssid)) != 0) {
Serial.printf("Found stronger AP %d %02X:%02X:%02X:%02X:%02X:%02X\n", channel, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); if(!this->connected()) {
this->changeAP(bssid, channel); Serial.printf("Connecting to AP %02X:%02X:%02X:%02X:%02X:%02X CH: %d\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
this->connectWiFi(bssid, channel);
}
else {
Serial.printf("Found stronger AP %02X:%02X:%02X:%02X:%02X:%02X CH: %d\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
this->changeAP(bssid, channel);
}
} }
} }
_apScanning = false; _apScanning = false;
} }
} }
} }
if(settings.ssdpBroadcast) { if(!this->connecting() && !settings.WIFI.hidden) {
if((this->softAPOpened && WiFi.softAPgetStationNum() == 0) ||
(!this->connected() && ctype == conn_types_t::wifi)) {
// If the Soft AP is opened and there are no clients connected then we need to scan for an AP. If
// our target exists we will exit out of the Soft AP and start that connection. We are also
// going to continuously scan when there is no connection and our preferred connection is wifi.
if(ctype == conn_types_t::wifi) {
// Scan for an AP but only if we are not already scanning.
if(!_apScanning && WiFi.scanNetworks(true, false, true, 300, 0, settings.WIFI.ssid) == -1) {
_apScanning = true;
}
}
}
else if(this->connected() && ctype == conn_types_t::wifi && settings.WIFI.roaming) {
// Periodically look for a roaming AP.
if(millis() > SSID_SCAN_INTERVAL + this->lastWifiScan) {
//Serial.println("Started scan for access points");
if(!_apScanning && WiFi.scanNetworks(true, false, true, 300, 0, settings.WIFI.ssid) == -1) {
_apScanning = true;
this->lastWifiScan = millis();
}
}
}
}
if(millis() - this->lastEmit > 1500) {
// Post our connection status if needed.
this->lastEmit = millis();
if(this->connected()) {
this->emitSockets();
this->lastEmit = millis();
}
esp_task_wdt_reset(); // Make sure we do not reboot here.
}
sockEmit.loop();
mqtt.loop();
if(settings.ssdpBroadcast && this->connected()) {
if(!SSDP.isStarted) SSDP.begin(); if(!SSDP.isStarted) SSDP.begin();
if(SSDP.isStarted) SSDP.loop(); if(SSDP.isStarted) SSDP.loop();
} }
else if(!settings.ssdpBroadcast && SSDP.isStarted) SSDP.end(); else if(!settings.ssdpBroadcast && SSDP.isStarted) SSDP.end();
mqtt.loop();
} }
bool Network::changeAP(const uint8_t *bssid, const int32_t channel) { bool Network::changeAP(const uint8_t *bssid, const int32_t channel) {
esp_task_wdt_reset(); // Make sure we do not reboot here.
if(SSDP.isStarted) SSDP.end(); if(SSDP.isStarted) SSDP.end();
mqtt.disconnect(); mqtt.disconnect();
sockEmit.end(); //sockEmit.end();
WiFi.disconnect(false, true); WiFi.disconnect(false, true);
this->connType = conn_types_t::unset;
this->_connecting = true;
this->connectStart = millis();
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid); WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid);
uint8_t retries = 0; this->connectStart = millis();
while(retries < 100) {
esp_task_wdt_reset(); // Make sure we do not reboot here.
wl_status_t stat = WiFi.status();
if(stat == WL_CONNECTED) {
Serial.println("WiFi module connected");
this->ssid = WiFi.SSID();
this->mac = WiFi.BSSIDstr();
this->strength = WiFi.RSSI();
this->channel = WiFi.channel();
return true;
}
else if(stat == WL_CONNECT_FAILED) {
Serial.println("WiFi Module connection failed");
return false;
}
else if(stat == WL_NO_SSID_AVAIL) {
Serial.println(" Connection failed the SSID ");
return false;
}
else if(stat == WL_NO_SHIELD) {
Serial.println("Connection failed - WiFi module not found");
return false;
}
else if(stat == WL_IDLE_STATUS) {
Serial.print("*");
}
else if(stat == WL_DISCONNECTED) {
Serial.print("-");
}
else {
Serial.printf("Unknown status %d\n", stat);
}
delay(300);
}
return false; return false;
} }
void Network::emitSockets() { void Network::emitSockets() {
this->emitHeap(); this->emitHeap();
if(this->needsBroadcast || if(this->needsBroadcast ||
(this->connType == conn_types::wifi && (abs(abs(WiFi.RSSI()) - abs(this->lastRSSI)) > 1 || WiFi.channel() != this->lastChannel))) { (this->connType == conn_types_t::wifi && (abs(abs(WiFi.RSSI()) - abs(this->lastRSSI)) > 1 || WiFi.channel() != this->lastChannel))) {
this->emitSockets(255); this->emitSockets(255);
sockEmit.loop(); sockEmit.loop();
this->needsBroadcast = false; this->needsBroadcast = false;
} }
} }
void Network::emitSockets(uint8_t num) { void Network::emitSockets(uint8_t num) {
//char buf[128]; if(this->connType == conn_types_t::ethernet) {
if(this->connType == conn_types::ethernet) {
JsonSockEvent *json = sockEmit.beginEmit("ethernet"); JsonSockEvent *json = sockEmit.beginEmit("ethernet");
json->beginObject(); json->beginObject();
json->addElem("connected", this->connected()); json->addElem("connected", this->connected());
@ -159,13 +185,6 @@ void Network::emitSockets(uint8_t num) {
json->addElem("fullduplex", ETH.fullDuplex()); json->addElem("fullduplex", ETH.fullDuplex());
json->endObject(); json->endObject();
sockEmit.endEmit(num); sockEmit.endEmit(num);
/*
snprintf(buf, sizeof(buf), "{\"connected\":%s,\"speed\":%d,\"fullduplex\":%s}", this->connected() ? "true" : "false", ETH.linkSpeed(), ETH.fullDuplex() ? "true" : "false");
if(num == 255)
sockEmit.sendToClients("ethernet", buf);
else
sockEmit.sendToClient(num, "ethernet", buf);
*/
} }
else { else {
if(WiFi.status() == WL_CONNECTED) { if(WiFi.status() == WL_CONNECTED) {
@ -176,13 +195,6 @@ void Network::emitSockets(uint8_t num) {
json->addElem("channel", (int32_t)this->channel); json->addElem("channel", (int32_t)this->channel);
json->endObject(); json->endObject();
sockEmit.endEmit(num); sockEmit.endEmit(num);
/*
snprintf(buf, sizeof(buf), "{\"ssid\":\"%s\",\"strength\":%d,\"channel\":%d}", WiFi.SSID().c_str(), WiFi.RSSI(), this->channel);
if(num == 255)
sockEmit.sendToClients("wifiStrength", buf);
else
sockEmit.sendToClient(num, "wifiStrength", buf);
*/
this->lastRSSI = WiFi.RSSI(); this->lastRSSI = WiFi.RSSI();
this->lastChannel = WiFi.channel(); this->lastChannel = WiFi.channel();
} }
@ -202,29 +214,20 @@ void Network::emitSockets(uint8_t num) {
json->addElem("fullduplex", false); json->addElem("fullduplex", false);
json->endObject(); json->endObject();
sockEmit.endEmit(num); sockEmit.endEmit(num);
/*
if(num == 255) {
sockEmit.sendToClients("wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}");
sockEmit.sendToClients("ethernet", "{\"connected\":false,\"speed\":0,\"fullduplex\":false}");
}
else {
sockEmit.sendToClient(num, "wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}");
sockEmit.sendToClient(num, "ethernet", "{\"connected\":false,\"speed\":0,\"fullduplex\":false}");
}
*/
this->lastRSSI = -100; this->lastRSSI = -100;
this->lastChannel = -1; this->lastChannel = -1;
} }
} }
this->emitHeap(num); this->emitHeap(num);
} }
void Network::setConnected(conn_types connType) { void Network::setConnected(conn_types_t connType) {
esp_task_wdt_reset();
this->connType = connType; this->connType = connType;
this->connectTime = millis(); this->connectTime = millis();
connectRetries = 0; connectRetries = 0;
if(this->connType == conn_types::wifi) { Serial.println("Setting connected...");
if(this->softAPOpened) { if(this->connType == conn_types_t::wifi) {
if(this->softAPOpened && WiFi.softAPgetStationNum() == 0) {
WiFi.softAPdisconnect(true); WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
} }
@ -233,20 +236,25 @@ void Network::setConnected(conn_types connType) {
this->mac = WiFi.BSSIDstr(); this->mac = WiFi.BSSIDstr();
this->strength = WiFi.RSSI(); this->strength = WiFi.RSSI();
this->channel = WiFi.channel(); this->channel = WiFi.channel();
this->connectAttempts++;
} }
else if(this->connType == conn_types::ethernet) { else if(this->connType == conn_types_t::ethernet) {
if(this->softAPOpened) { if(this->softAPOpened) {
Serial.println("Disonnecting from SoftAP"); Serial.println("Disonnecting from SoftAP");
WiFi.softAPdisconnect(true); WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
} }
this->connectAttempts++;
this->_connecting = false; this->_connecting = false;
this->wifiFallback = false; this->wifiFallback = false;
} }
sockEmit.begin(); // NET: Begin this in the startup.
//sockEmit.begin();
esp_task_wdt_reset();
if(this->connectAttempts == 1) { if(this->connectAttempts == 1) {
Serial.println(); Serial.println();
if(this->connType == conn_types::wifi) { if(this->connType == conn_types_t::wifi) {
Serial.print("Successfully Connected to WiFi!!!!"); Serial.print("Successfully Connected to WiFi!!!!");
Serial.print(WiFi.localIP()); Serial.print(WiFi.localIP());
Serial.print(" ("); Serial.print(" (");
@ -276,6 +284,7 @@ void Network::setConnected(conn_types connType) {
settings.IP.dns1 = ETH.dnsIP(0); settings.IP.dns1 = ETH.dnsIP(0);
settings.IP.dns2 = ETH.dnsIP(1); settings.IP.dns2 = ETH.dnsIP(1);
} }
esp_task_wdt_reset();
JsonSockEvent *json = sockEmit.beginEmit("ethernet"); JsonSockEvent *json = sockEmit.beginEmit("ethernet");
json->beginObject(); json->beginObject();
json->addElem("connected", this->connected()); json->addElem("connected", this->connected());
@ -283,11 +292,7 @@ void Network::setConnected(conn_types connType) {
json->addElem("fullduplex", ETH.fullDuplex()); json->addElem("fullduplex", ETH.fullDuplex());
json->endObject(); json->endObject();
sockEmit.endEmit(); sockEmit.endEmit();
/* esp_task_wdt_reset();
char buf[128];
snprintf(buf, sizeof(buf), "{\"connected\":true,\"speed\":%d,\"fullduplex\":%s}", ETH.linkSpeed(), ETH.fullDuplex() ? "true" : "false");
sockEmit.sendToClients("ethernet", buf);
*/
} }
} }
else { else {
@ -295,7 +300,7 @@ void Network::setConnected(conn_types connType) {
Serial.print("Reconnected after "); Serial.print("Reconnected after ");
Serial.print(1.0 * (millis() - this->connectStart)/1000); Serial.print(1.0 * (millis() - this->connectStart)/1000);
Serial.print("sec IP: "); Serial.print("sec IP: ");
if(this->connType == conn_types::wifi) { if(this->connType == conn_types_t::wifi) {
Serial.print(WiFi.localIP()); Serial.print(WiFi.localIP());
Serial.print(" "); Serial.print(" ");
Serial.print(this->mac); Serial.print(this->mac);
@ -338,6 +343,7 @@ void Network::setConnected(conn_types connType) {
SSDP.setManufacturerURL(0, "https://github.com/rstrouse"); SSDP.setManufacturerURL(0, "https://github.com/rstrouse");
SSDP.setURL(0, "/"); SSDP.setURL(0, "/");
SSDP.setActive(0, true); SSDP.setActive(0, true);
esp_task_wdt_reset();
if(MDNS.begin(settings.hostname)) { if(MDNS.begin(settings.hostname)) {
Serial.printf("MDNS Responder Started: serverId=%s\n", settings.serverId); Serial.printf("MDNS Responder Started: serverId=%s\n", settings.serverId);
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);
@ -350,9 +356,11 @@ void Network::setConnected(conn_types connType) {
MDNS.addServiceTxt("espsomfy_rts", "tcp", "version", String(settings.fwVersion.name)); MDNS.addServiceTxt("espsomfy_rts", "tcp", "version", String(settings.fwVersion.name));
} }
if(settings.ssdpBroadcast) { if(settings.ssdpBroadcast) {
esp_task_wdt_reset();
SSDP.begin(); SSDP.begin();
} }
else if(SSDP.isStarted) SSDP.end(); else if(SSDP.isStarted) SSDP.end();
esp_task_wdt_reset();
this->emitSockets(); this->emitSockets();
settings.printAvailHeap(); settings.printAvailHeap();
this->needsBroadcast = true; this->needsBroadcast = true;
@ -361,16 +369,16 @@ bool Network::connectWired() {
if(ETH.linkUp()) { if(ETH.linkUp()) {
// If the ethernet link is re-established then we need to shut down wifi. // If the ethernet link is re-established then we need to shut down wifi.
if(WiFi.status() == WL_CONNECTED) { if(WiFi.status() == WL_CONNECTED) {
sockEmit.end(); //sockEmit.end();
WiFi.disconnect(true); WiFi.disconnect(true);
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
} }
if(this->connType != conn_types::ethernet) this->setConnected(conn_types::ethernet); if(this->connType != conn_types_t::ethernet) this->setConnected(conn_types_t::ethernet);
this->wifiFallback = false;
return true; return true;
} }
else if(this->ethStarted) { else if(this->ethStarted) {
if(settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0') // There is no wired connection so we need to fallback if appropriate.
if(settings.connType == conn_types_t::ethernetpref && settings.WIFI.ssid[0] != '\0')
return this->connectWiFi(); return this->connectWiFi();
} }
if(this->connectAttempts > 0) { if(this->connectAttempts > 0) {
@ -379,47 +387,45 @@ bool Network::connectWired() {
} }
else else
Serial.println("Connecting to Wired Ethernet"); Serial.println("Connecting to Wired Ethernet");
this->connectAttempts++;
this->_connecting = true; this->_connecting = true;
this->connTarget = conn_types::ethernet; this->connTarget = conn_types_t::ethernet;
this->connType = conn_types::unset; this->connType = conn_types_t::unset;
if(!this->ethStarted) { if(!this->ethStarted) {
this->ethStarted = true; // Currently the ethernet module will leak memory if you call begin more than once.
WiFi.mode(WIFI_OFF); this->ethStarted = true;
if(settings.hostname[0] != '\0') WiFi.mode(WIFI_OFF);
ETH.setHostname(settings.hostname); if(settings.hostname[0] != '\0')
else ETH.setHostname(settings.hostname);
ETH.setHostname("ESPSomfy-RTS"); else
ETH.setHostname("ESPSomfy-RTS");
Serial.print("Set hostname to:"); Serial.print("Set hostname to:");
Serial.println(ETH.getHostname()); Serial.println(ETH.getHostname());
if(!ETH.begin(settings.Ethernet.phyAddress, settings.Ethernet.PWRPin, settings.Ethernet.MDCPin, settings.Ethernet.MDIOPin, settings.Ethernet.phyType, settings.Ethernet.CLKMode)) { if(!ETH.begin(settings.Ethernet.phyAddress, settings.Ethernet.PWRPin, settings.Ethernet.MDCPin, settings.Ethernet.MDIOPin, settings.Ethernet.phyType, settings.Ethernet.CLKMode)) {
Serial.println("Ethernet Begin failed"); Serial.println("Ethernet Begin failed");
this->ethStarted = false; this->ethStarted = false;
if(settings.connType == conn_types::ethernetpref) { if(settings.connType == conn_types_t::ethernetpref) {
this->wifiFallback = true; this->wifiFallback = true;
return connectWiFi(); return connectWiFi();
}
return false;
} }
else { return false;
if(!settings.IP.dhcp) { }
if(!ETH.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2)) { else {
Serial.println("Unable to configure static IP address...."); if(!settings.IP.dhcp) {
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); if(!ETH.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2)) {
} Serial.println("Unable to configure static IP address....");
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
} }
else
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
} }
else
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
}
} }
this->connectStart = millis(); this->connectStart = millis();
return true; return true;
} }
void Network::updateHostname() { void Network::updateHostname() {
if(settings.hostname[0] != '\0' && this->connected()) { if(settings.hostname[0] != '\0' && this->connected()) {
if(this->connType == conn_types::ethernet && if(this->connType == conn_types_t::ethernet &&
strcmp(settings.hostname, ETH.getHostname()) != 0) { strcmp(settings.hostname, ETH.getHostname()) != 0) {
Serial.printf("Updating host name to %s...\n", settings.hostname); Serial.printf("Updating host name to %s...\n", settings.hostname);
ETH.setHostname(settings.hostname); ETH.setHostname(settings.hostname);
@ -434,13 +440,49 @@ void Network::updateHostname() {
} }
} }
} }
bool Network::connectWiFi() { bool Network::connectWiFi(const uint8_t *bssid, const int32_t channel) {
if(settings.WIFI.ssid[0] != '\0') { if(this->softAPOpened && WiFi.softAPgetStationNum() > 0) {
if(WiFi.status() == WL_CONNECTED && WiFi.SSID().compareTo(settings.WIFI.ssid) == 0) { // There is a client connected to the soft AP. We do not want to close out the connection. While both the
// If we are connected to the target SSID then just return. // Soft AP and a wifi connection can coexist on ESP32 the performance is abysmal.
WiFi.disconnect(false);
this->_connecting = false;
this->connType = conn_types_t::unset;
return true;
}
WiFi.setSleep(false);
if(!settings.IP.dhcp) {
if(!WiFi.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2))
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
}
else
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
delay(100);
if(bssid && channel > 0) {
if(WiFi.status() == WL_CONNECTED && WiFi.SSID().compareTo(settings.WIFI.ssid) == 0
&& WiFi.channel() == channel) {
this->disconnected = 0; this->disconnected = 0;
return true; return true;
} }
this->connTarget = conn_types_t::wifi;
this->connType = conn_types_t::unset;
Serial.println("WiFi begin...");
this->_connecting = true;
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid);
this->connectStart = millis();
}
else if(settings.WIFI.ssid[0] != '\0') {
if(WiFi.status() == WL_CONNECTED && WiFi.SSID().compareTo(settings.WIFI.ssid) == 0) {
// If we are connected to the target SSID then just return.
this->disconnected = 0;
this->_connecting = true;
return true;
}
if(this->_connecting) return true;
this->_connecting = true;
this->connTarget = conn_types_t::wifi;
this->connType = conn_types_t::unset;
if(this->connectAttempts > 0) { if(this->connectAttempts > 0) {
Serial.print("Connection Lost..."); Serial.print("Connection Lost...");
Serial.print(this->mac); Serial.print(this->mac);
@ -451,62 +493,49 @@ bool Network::connectWiFi() {
Serial.println("dbm) "); Serial.println("dbm) ");
} }
else Serial.println("Connecting to AP"); else Serial.println("Connecting to AP");
this->connectAttempts++;
this->_connecting = true;
this->connTarget = conn_types::wifi;
this->connType = conn_types::unset;
WiFi.setSleep(false);
WiFi.mode(WIFI_MODE_NULL);
if(!settings.IP.dhcp) {
if(!WiFi.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2))
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
}
else
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
delay(100); delay(100);
// There is also another method simply called hostname() but this is legacy for esp8266. // There is also another method simply called hostname() but this is legacy for esp8266.
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname); if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
Serial.print("Set hostname to:"); Serial.print("Set hostname to:");
Serial.println(WiFi.getHostname()); Serial.println(WiFi.getHostname());
WiFi.mode(WIFI_STA);
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
uint8_t bssid[6]; uint8_t _bssid[6];
int32_t channel = 0; int32_t _channel = 0;
if(this->getStrongestAP(settings.WIFI.ssid, bssid, &channel)) { if(!settings.WIFI.hidden && this->getStrongestAP(settings.WIFI.ssid, _bssid, &_channel)) {
Serial.printf("Found strongest AP %d %02X:%02X:%02X:%02X:%02X:%02X\n", channel, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); Serial.printf("Found strongest AP %02X:%02X:%02X:%02X:%02X:%02X CH:%d\n", _bssid[0], _bssid[1], _bssid[2], _bssid[3], _bssid[4], _bssid[5], _channel);
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid); WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, _channel, _bssid);
} }
else else
// If the user has the hidden flag set just connect to whatever the AP gives us.
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase); WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase);
this->connectStart = millis();
} }
this->connectStart = millis();
return true; return true;
} }
bool Network::connect() { bool Network::connect(conn_types_t ctype) {
if(this->connecting()) { esp_task_wdt_reset();
if(this->connType == conn_types::unset) { if(this->connecting()) return true;
// If we reached our timeout for the connection then we need to open the soft ap. if(this->disconnectTime == 0) this->disconnectTime = millis();
if(millis() > this->connectStart + CONNECT_TIMEOUT) { if(ctype == conn_types_t::ethernet && this->connType != conn_types_t::ethernet) {
if(this->connTarget == conn_types::ethernet && settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0') // Here we need to call the connect to ethernet.
this->connectWiFi();
else {
Serial.println("Fell into timeout");
this->openSoftAP();
}
}
}
else
this->setConnected(this->connTarget);
}
else if(settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref)
this->connectWired(); this->connectWired();
else if(settings.connType == conn_types::wifi && strlen(settings.WIFI.ssid) > 0) }
else if(ctype == conn_types_t::ap || (!this->connected() && millis() > this->disconnectTime + CONNECT_TIMEOUT)) {
if(!this->softAPOpened && !this->openingSoftAP) {
this->disconnectTime = millis();
this->openSoftAP();
}
else if(this->softAPOpened && !this->openingSoftAP &&
(ctype == conn_types_t::wifi && this->connType != conn_types_t::wifi && settings.WIFI.hidden)) {
// When thge softAP is open then we need to try to connect to wifi repeatedly if the user connects to a hidden SSID.
this->connectWiFi();
}
}
else if((ctype == conn_types_t::wifi && this->connType != conn_types_t::wifi && settings.WIFI.hidden)) {
this->connectWiFi(); this->connectWiFi();
else }
this->openSoftAP();
return true; return true;
} }
uint32_t Network::getChipId() { uint32_t Network::getChipId() {
@ -517,16 +546,16 @@ uint32_t Network::getChipId() {
} }
return chipId; return chipId;
} }
bool Network::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel) { bool Network::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel) {
// The new AP must be at least 10dbm greater. // The new AP must be at least 10dbm greater.
int32_t strength = this->connected() ? WiFi.RSSI() + 10 : -127; int32_t strength = this->connected() ? WiFi.RSSI() + 10 : -127;
int32_t chan = -1; int32_t chan = -1;
memset(bssid, 0x00, 6); memset(bssid, 0x00, 6);
esp_task_wdt_delete(NULL); esp_task_wdt_delete(NULL);
uint8_t n = this->connected() ? WiFi.scanComplete() : WiFi.scanNetworks(false, false, false, 300, 0, ssid); int16_t n = WiFi.scanComplete();
//int16_t n = this->connected() ? WiFi.scanComplete() : WiFi.scanNetworks(false, false, false, 300, 0, ssid);
esp_task_wdt_add(NULL); esp_task_wdt_add(NULL);
for(uint8_t i = 0; i < n; i++) { for(int16_t i = 0; i < n; i++) {
if(WiFi.SSID(i).compareTo(ssid) == 0) { if(WiFi.SSID(i).compareTo(ssid) == 0) {
if(WiFi.RSSI(i) > strength) { if(WiFi.RSSI(i) > strength) {
strength = WiFi.RSSI(i); strength = WiFi.RSSI(i);
@ -539,98 +568,56 @@ bool Network::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel)
return chan > 0; return chan > 0;
} }
bool Network::openSoftAP() { bool Network::openSoftAP() {
if(this->softAPOpened || this->openingSoftAP) return true;
if(this->connected()) WiFi.disconnect(false);
this->openingSoftAP = true;
Serial.println(); Serial.println();
Serial.println("Turning the HotSpot On"); Serial.println("Turning the HotSpot On");
WiFi.disconnect(true); esp_task_wdt_reset(); // Make sure we do not reboot here.
WiFi.hostname("ESPSomfy RTS"); WiFi.softAP(strlen(settings.hostname) > 0 ? settings.hostname : "ESPSomfy RTS", "");
WiFi.mode(WIFI_AP_STA); delay(200);
this->_connecting = false;
delay(100);
WiFi.softAP("ESPSomfy RTS", "");
Serial.println("Initializing AP for credentials modification");
Serial.println();
Serial.print("SoftAP IP: ");
Serial.println(WiFi.softAPIP());
//pinMode(D0, INPUT_PULLUP);
long startTime = millis();
int c = 0;
sockEmit.begin();
while (!this->connected())
{
int clients = WiFi.softAPgetStationNum();
somfy.loop();
webServer.loop();
if(millis() - this->lastEmit > 1500) {
//if(this->connect()) {}
this->lastEmit = millis();
this->emitSockets();
if(clients > 0)
Serial.print(clients);
else
Serial.print(".");
c++;
}
sockEmit.loop();
if(rebootDelay.reboot && millis() > rebootDelay.rebootTime) {
this->end();
ESP.restart();
break;
}
// If no clients have connected in 3 minutes from starting this server reboot this pig. This will
// force a reboot cycle until we have some response. That is unless the SSID has been cleared.
if(clients == 0 && this->connType != conn_types::unset) {
Serial.println();
Serial.println("Connection Established Stopping AP Mode");
this->setConnected(this->connType);
return false;
}
else if(clients == 0 &&
(strlen(settings.WIFI.ssid) > 0 || settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref) &&
millis() - startTime > 3 * 60000) {
Serial.println();
Serial.println("Stopping AP Mode");
WiFi.softAPdisconnect(true);
return false;
}
if(c == 100) {
Serial.println();
c = 0;
}
esp_task_wdt_reset();
yield();
}
return true; return true;
} }
bool Network::connected() { bool Network::connected() {
if(this->connecting()) return false; if(this->connecting()) return false;
else if(this->connType == conn_types::unset) return false; else if(this->connType == conn_types_t::unset) return false;
else if(this->connType == conn_types::wifi) return WiFi.status() == WL_CONNECTED; else if(this->connType == conn_types_t::wifi) return WiFi.status() == WL_CONNECTED;
else if(this->connType == conn_types::ethernet) return ETH.linkUp(); else if(this->connType == conn_types_t::ethernet) return ETH.linkUp();
else return this->connType != conn_types::unset; else return this->connType != conn_types_t::unset;
return false; return false;
} }
bool Network::connecting() { bool Network::connecting() {
if(this->_connecting && millis() > this->connectStart + CONNECT_TIMEOUT) this->_connecting = false;
return this->_connecting; return this->_connecting;
} }
void Network::clearConnecting() { this->_connecting = false; }
void Network::networkEvent(WiFiEvent_t event) { void Network::networkEvent(WiFiEvent_t event) {
switch(event) { switch(event) {
case ARDUINO_EVENT_WIFI_READY: Serial.println("WiFi interface ready"); break; case ARDUINO_EVENT_WIFI_READY: Serial.println("(evt) WiFi interface ready"); break;
case ARDUINO_EVENT_WIFI_SCAN_DONE: Serial.println("Completed scan for access points"); break; case ARDUINO_EVENT_WIFI_SCAN_DONE:
Serial.printf("(evt) Completed scan for access points (%d)\n", WiFi.scanComplete());
//Serial.println("(evt) Completed scan for access points");
net.lastWifiScan = millis();
break;
case ARDUINO_EVENT_WIFI_STA_START: case ARDUINO_EVENT_WIFI_STA_START:
Serial.println("WiFi station mode started"); Serial.println("WiFi station mode started");
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname); if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
break; break;
case ARDUINO_EVENT_WIFI_STA_STOP: Serial.println("WiFi clients stopped"); break; case ARDUINO_EVENT_WIFI_STA_STOP: Serial.println("(evt) WiFi clients stopped"); break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("Connected to access point"); break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("(evt) Connected to WiFi STA access point"); break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: Serial.println("Disconnected from WiFi access point"); break; case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: Serial.println("Authentication mode of access point has changed"); break; Serial.printf("(evt) Disconnected from WiFi STA access point. Connecting: %d\n", net.connecting());
net.connType = conn_types_t::unset;
net.disconnectTime = millis();
net.clearConnecting();
break;
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: Serial.println("(evt) Authentication mode of STA access point has changed"); break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP:
Serial.print("Got WiFi IP: "); Serial.print("(evt) Got WiFi STA IP: ");
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
net.connType = conn_types::wifi; net.connType = conn_types_t::wifi;
net.connectTime = millis();
net.setConnected(conn_types_t::wifi);
break; break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP: Serial.println("Lost IP address and IP address is reset to 0"); break; case ARDUINO_EVENT_WIFI_STA_LOST_IP: Serial.println("Lost IP address and IP address is reset to 0"); break;
case ARDUINO_EVENT_ETH_GOT_IP: case ARDUINO_EVENT_ETH_GOT_IP:
@ -638,52 +625,87 @@ void Network::networkEvent(WiFiEvent_t event) {
if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true); if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true);
Serial.print("Got Ethernet IP "); Serial.print("Got Ethernet IP ");
Serial.println(ETH.localIP()); Serial.println(ETH.localIP());
net.connType = conn_types::ethernet; net.connectTime = millis();
net.connType = conn_types_t::ethernet;
if(settings.IP.dhcp) {
settings.IP.ip = ETH.localIP();
settings.IP.subnet = ETH.subnetMask();
settings.IP.gateway = ETH.gatewayIP();
settings.IP.dns1 = ETH.dnsIP(0);
settings.IP.dns2 = ETH.dnsIP(1);
}
net.setConnected(conn_types_t::ethernet);
break; break;
case ARDUINO_EVENT_ETH_CONNECTED: case ARDUINO_EVENT_ETH_CONNECTED:
Serial.print("Ethernet Connected "); Serial.print("(evt) Ethernet Connected ");
// We don't want to call setConnected if we do not have an IP address yet
//if(ETH.localIP() != INADDR_NONE)
// net.setConnected(conn_types::ethernet);
break; break;
case ARDUINO_EVENT_ETH_DISCONNECTED: case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("Ethernet Disconnected"); Serial.println("(evt) Ethernet Disconnected");
net.connType = conn_types::unset; net.connType = conn_types_t::unset;
net.disconnectTime = millis();
net.clearConnecting();
break; break;
case ARDUINO_EVENT_ETH_START: case ARDUINO_EVENT_ETH_START:
Serial.println("Ethernet Started"); Serial.println("(evt) Ethernet Started");
net.ethStarted = true; net.ethStarted = true;
break; break;
case ARDUINO_EVENT_ETH_STOP: case ARDUINO_EVENT_ETH_STOP:
Serial.println("Ethernet Stopped"); Serial.println("(evt) Ethernet Stopped");
net.connType = conn_types::unset; net.connType = conn_types_t::unset;
net.ethStarted = false; net.ethStarted = false;
break; break;
case ARDUINO_EVENT_WIFI_AP_START: case ARDUINO_EVENT_WIFI_AP_START:
Serial.println("WiFi AP Started"); Serial.print("(evt) WiFi SoftAP Started IP:");
Serial.println(WiFi.softAPIP());
net.openingSoftAP = false;
net.softAPOpened = true; net.softAPOpened = true;
break; break;
case ARDUINO_EVENT_WIFI_AP_STOP: case ARDUINO_EVENT_WIFI_AP_STOP:
Serial.println("WiFi AP Stopped"); if(!net.openingSoftAP) Serial.println("(evt) WiFi SoftAP Stopped");
net.softAPOpened = false; net.softAPOpened = false;
break; break;
default: default:
if(event > ARDUINO_EVENT_ETH_START) if(event > ARDUINO_EVENT_ETH_START)
Serial.printf("Unknown Ethernet Event %d\n", event); Serial.printf("(evt) Unknown Ethernet Event %d\n", event);
break; break;
} }
} }
void Network::emitHeap(uint8_t num) { void Network::emitHeap(uint8_t num) {
if(num != 255 || this->needsBroadcast || (ESP.getMaxAllocHeap() != _lastMaxHeap || ESP.getFreeHeap() != _lastHeap)) { bool bEmit = false;
_lastMaxHeap = ESP.getMaxAllocHeap(); bool bTimeEmit = millis() - _lastHeapEmit > 15000;
_lastHeap = ESP.getFreeHeap(); bool bRoomEmit = false;
bool bValEmit = false;
if(num != 255 || this->needsBroadcast) bEmit = true;
if(millis() - _lastHeapEmit > 15000) bTimeEmit = true;
uint32_t freeHeap = ESP.getFreeHeap();
uint32_t maxHeap = ESP.getMaxAllocHeap();
uint32_t minHeap = ESP.getMinFreeHeap();
if(abs((int)(freeHeap - _lastHeap)) > 1500) bValEmit = true;
if(abs((int)(maxHeap - _lastMaxHeap)) > 1500) bValEmit = true;
bRoomEmit = sockEmit.activeClients(0) > 0;
if(bValEmit) bTimeEmit = millis() - _lastHeapEmit > 7000;
if(bEmit || bTimeEmit || bRoomEmit || bValEmit) {
JsonSockEvent *json = sockEmit.beginEmit("memStatus"); JsonSockEvent *json = sockEmit.beginEmit("memStatus");
json->beginObject(); json->beginObject();
json->addElem("max", _lastMaxHeap); json->addElem("max", maxHeap);
json->addElem("free", _lastHeap); json->addElem("free", freeHeap);
json->addElem("min", ESP.getMinFreeHeap()); json->addElem("min", minHeap);
json->addElem("total", ESP.getHeapSize()); json->addElem("total", ESP.getHeapSize());
json->endObject(); json->endObject();
sockEmit.endEmit(num); if(num == 255 && bTimeEmit && bValEmit) {
sockEmit.endEmit(num);
_lastHeapEmit = millis();
_lastHeap = freeHeap;
_lastMaxHeap = maxHeap;
//Serial.printf("BROAD HEAP: Emit:%d TimeEmit:%d ValEmit:%d\n", bEmit, bTimeEmit, bValEmit);
}
else if(num != 255) {
sockEmit.endEmit(num);
//Serial.printf("TARGET HEAP %d: Emit:%d TimeEmit:%d ValEmit:%d\n", num, bEmit, bTimeEmit, bValEmit);
}
else if(bRoomEmit) {
sockEmit.endEmitRoom(0);
//Serial.printf("ROOM HEAP: Emit:%d TimeEmit:%d ValEmit:%d\n", bEmit, bTimeEmit, bValEmit);
}
} }
} }

View file

@ -3,7 +3,10 @@
#ifndef Network_h #ifndef Network_h
#define Network_h #define Network_h
//enum class conn_types_t : byte;
#define CONNECT_TIMEOUT 20000 #define CONNECT_TIMEOUT 20000
#define SSID_SCAN_INTERVAL 60000
class Network { class Network {
protected: protected:
unsigned long lastEmit = 0; unsigned long lastEmit = 0;
@ -13,27 +16,32 @@ class Network {
int linkSpeed = 0; int linkSpeed = 0;
bool _connecting = false; bool _connecting = false;
public: public:
unsigned long lastWifiScan = 0;
bool ethStarted = false; bool ethStarted = false;
bool wifiFallback = false; bool wifiFallback = false;
bool softAPOpened = false; bool softAPOpened = false;
bool openingSoftAP = false;
bool needsBroadcast = true; bool needsBroadcast = true;
conn_types connType = conn_types::unset; conn_types_t connType = conn_types_t::unset;
conn_types connTarget = conn_types::unset; conn_types_t connTarget = conn_types_t::unset;
bool connected(); bool connected();
bool connecting(); bool connecting();
void clearConnecting();
conn_types_t preferredConnType();
String ssid; String ssid;
String mac; String mac;
int channel; int channel;
int strength; int strength;
int disconnected = 0; int disconnected = 0;
int connectAttempts = 0; int connectAttempts = 0;
uint32_t disconnectTime = 0;
uint32_t connectStart = 0; uint32_t connectStart = 0;
uint32_t connectTime = 0; uint32_t connectTime = 0;
bool openSoftAP(); bool openSoftAP();
bool connect(); bool connect(conn_types_t ctype);
bool connectWiFi(); bool connectWiFi(const uint8_t *bssid = nullptr, const int32_t channel = -1);
bool connectWired(); bool connectWired();
void setConnected(conn_types connType); void setConnected(conn_types_t connType);
bool getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel); bool getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel);
bool changeAP(const uint8_t *bssid, const int32_t channel); bool changeAP(const uint8_t *bssid, const int32_t channel);
//int getStrengthByMac(const char *mac); //int getStrengthByMac(const char *mac);

View file

@ -161,7 +161,7 @@ void UPNPDeviceType::setChipId(uint32_t chipId) {
(uint16_t)chipId & 0xff); (uint16_t)chipId & 0xff);
} }
SSDPClass::SSDPClass():sendQueue{false, INADDR_NONE, 0, nullptr, false, 0, "", response_types_t::root} {} SSDPClass::SSDPClass():sendQueue{false, INADDR_NONE, 0, nullptr, false, 0, "", response_types_t::root} {}
SSDPClass::~SSDPClass() { end(); } SSDPClass::~SSDPClass() { end(); this->isStarted = false; }
bool SSDPClass::begin() { bool SSDPClass::begin() {
for(int i = 0; i < SSDP_QUEUE_SIZE; i++) { for(int i = 0; i < SSDP_QUEUE_SIZE; i++) {
this->sendQueue[i].waiting = false; this->sendQueue[i].waiting = false;
@ -209,6 +209,7 @@ void SSDPClass::end() {
if(this->_server.connected()) { if(this->_server.connected()) {
this->_sendByeBye(); this->_sendByeBye();
this->_server.close(); this->_server.close();
Serial.println("Disconnected from SSDP...");
} }
this->isStarted = false; this->isStarted = false;
// Clear out the last notified so if the user starts us up again it will notify // Clear out the last notified so if the user starts us up again it will notify
@ -216,8 +217,6 @@ void SSDPClass::end() {
for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) { for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) {
this->deviceTypes[i].lastNotified = 0; this->deviceTypes[i].lastNotified = 0;
} }
Serial.println("Disconnected from SSDP...");
} }
UPNPDeviceType* SSDPClass::getDeviceType(uint8_t ndx) { if(ndx < this->m_cdeviceTypes) return &this->deviceTypes[ndx]; return nullptr; } UPNPDeviceType* SSDPClass::getDeviceType(uint8_t ndx) { if(ndx < this->m_cdeviceTypes) return &this->deviceTypes[ndx]; return nullptr; }
UPNPDeviceType* SSDPClass::findDeviceByType(char *devType) { UPNPDeviceType* SSDPClass::findDeviceByType(char *devType) {

View file

@ -1,6 +1,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <WebSocketsServer.h> #include <WebSocketsServer.h>
#include <esp_task_wdt.h>
#include "Sockets.h" #include "Sockets.h"
#include "ConfigSettings.h" #include "ConfigSettings.h"
#include "Somfy.h" #include "Somfy.h"
@ -42,6 +43,9 @@ bool room_t::leave(uint8_t num) {
} }
return true; return true;
} }
void room_t::clear() {
memset(this->clients, 255, sizeof(this->clients));
}
uint8_t room_t::activeClients() { uint8_t room_t::activeClients() {
uint8_t n = 0; uint8_t n = 0;
for(uint8_t i = 0; i < sizeof(this->clients); i++) { for(uint8_t i = 0; i < sizeof(this->clients); i++) {
@ -76,7 +80,7 @@ void SocketEmitter::begin() {
sockServer.enableHeartbeat(20000, 10000, 3); sockServer.enableHeartbeat(20000, 10000, 3);
sockServer.onEvent(this->wsEvent); sockServer.onEvent(this->wsEvent);
Serial.println("Socket Server Started..."); Serial.println("Socket Server Started...");
settings.printAvailHeap(); //settings.printAvailHeap();
} }
void SocketEmitter::loop() { void SocketEmitter::loop() {
this->initClients(); this->initClients();
@ -105,10 +109,12 @@ void SocketEmitter::initClients() {
if(num != 255) { if(num != 255) {
if(sockServer.clientIsConnected(num)) { if(sockServer.clientIsConnected(num)) {
Serial.printf("Initializing Socket Client %u\n", num); Serial.printf("Initializing Socket Client %u\n", num);
esp_task_wdt_reset();
settings.emitSockets(num); settings.emitSockets(num);
somfy.emitState(num); somfy.emitState(num);
git.emitUpdateCheck(num); git.emitUpdateCheck(num);
net.emitSockets(num); net.emitSockets(num);
esp_task_wdt_reset();
} }
this->newClients[i] = 255; this->newClients[i] = 255;
} }
@ -123,7 +129,11 @@ void SocketEmitter::delayInit(uint8_t num) {
} }
} }
} }
void SocketEmitter::end() { sockServer.close(); } void SocketEmitter::end() {
sockServer.close();
for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++)
this->rooms[i].clear();
}
void SocketEmitter::disconnect() { sockServer.disconnect(); } void SocketEmitter::disconnect() { sockServer.disconnect(); }
void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) { void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
switch(type) { switch(type) {

View file

@ -12,6 +12,7 @@ struct room_t {
bool isJoined(uint8_t num); bool isJoined(uint8_t num);
bool join(uint8_t num); bool join(uint8_t num);
bool leave(uint8_t num); bool leave(uint8_t num);
void clear();
}; };
class SocketEmitter { class SocketEmitter {
protected: protected:

104
Somfy.cpp
View file

@ -257,6 +257,10 @@ void somfy_frame_t::decodeFrame(somfy_rx_t *rx) {
this->rssi = ELECHOUSE_cc1101.getRssi(); this->rssi = ELECHOUSE_cc1101.getRssi();
this->decodeFrame(rx->payload); this->decodeFrame(rx->payload);
} }
byte somfy_frame_t::encode80Byte7(byte start, uint8_t repeat) {
while((repeat * 4) + start > 255) repeat -= 15;
return start + (repeat * 4);
}
void somfy_frame_t::encode80BitFrame(byte *frame, uint8_t repeat) { void somfy_frame_t::encode80BitFrame(byte *frame, uint8_t repeat) {
switch(this->cmd) { switch(this->cmd) {
// Step up and down commands encode the step size into the last 3 bytes. // Step up and down commands encode the step size into the last 3 bytes.
@ -291,28 +295,44 @@ void somfy_frame_t::encode80BitFrame(byte *frame, uint8_t repeat) {
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]); frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
break; break;
case somfy_commands::Toggle: case somfy_commands::Toggle:
if(repeat == 0) { frame[0] = 164;
frame[0] = 164; frame[1] |= 0xF0;
frame[1] |= 0xF0; frame[7] = this->encode80Byte7(196, repeat);
}
frame[7] = 196;
frame[8] = 0; frame[8] = 0;
frame[9] = 0x10; frame[9] = 0x10;
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]); frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
break; break;
case somfy_commands::Prog: case somfy_commands::Up:
if(repeat > 0) frame[7] = 196 + (repeat * 4); frame[7] = this->encode80Byte7(196, repeat);
else frame[7] = 132; frame[8] = 32;
frame[8] = 0; frame[9] = 0x00;
frame[9] = ((repeat + 1) & 0x0F) << 4;
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]); frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
break; break;
case somfy_commands::Down:
frame[7] = this->encode80Byte7(196, repeat);
frame[8] = 44;
frame[9] = 0x80;
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
break;
case somfy_commands::Prog:
case somfy_commands::UpDown:
case somfy_commands::MyDown:
case somfy_commands::MyUp:
case somfy_commands::MyUpDown:
case somfy_commands::My:
frame[7] = this->encode80Byte7(196, repeat);
frame[8] = 0x00;
frame[9] = 0x10;
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
break;
default: default:
break; break;
} }
} }
void somfy_frame_t::encodeFrame(byte *frame) { void somfy_frame_t::encodeFrame(byte *frame) {
const byte btn = static_cast<byte>(cmd); const byte btn = static_cast<byte>(cmd);
this->valid = true;
frame[0] = this->encKey; // Encryption key. Doesn't matter much frame[0] = this->encKey; // Encryption key. Doesn't matter much
frame[1] = (btn & 0x0F) << 4; // Which button did you press? The 4 LSB will be the checksum frame[1] = (btn & 0x0F) << 4; // Which button did you press? The 4 LSB will be the checksum
frame[2] = this->rollingCode >> 8; // Rolling code (big endian) frame[2] = this->rollingCode >> 8; // Rolling code (big endian)
@ -400,7 +420,7 @@ void somfy_frame_t::encodeFrame(byte *frame) {
} }
else { else {
if(this->bitLength == 80) this->encode80BitFrame(&frame[0], 0); if(this->bitLength == 80) this->encode80BitFrame(&frame[0], this->repeats);
} }
byte checksum = 0; byte checksum = 0;
@ -432,6 +452,7 @@ void somfy_frame_t::print() {
Serial.print(" CS:"); Serial.print(" CS:");
Serial.println(this->checksum); Serial.println(this->checksum);
} }
bool somfy_frame_t::isSynonym(somfy_frame_t &frame) { return this->remoteAddress == frame.remoteAddress && this->cmd != frame.cmd && this->rollingCode == frame.rollingCode; }
bool somfy_frame_t::isRepeat(somfy_frame_t &frame) { return this->remoteAddress == frame.remoteAddress && this->cmd == frame.cmd && this->rollingCode == frame.rollingCode; } bool somfy_frame_t::isRepeat(somfy_frame_t &frame) { return this->remoteAddress == frame.remoteAddress && this->cmd == frame.cmd && this->rollingCode == frame.rollingCode; }
void somfy_frame_t::copy(somfy_frame_t &frame) { void somfy_frame_t::copy(somfy_frame_t &frame) {
if(this->isRepeat(frame)) { if(this->isRepeat(frame)) {
@ -440,8 +461,9 @@ void somfy_frame_t::copy(somfy_frame_t &frame) {
this->lqi = frame.lqi; this->lqi = frame.lqi;
} }
else { else {
this->synonym = this->isSynonym(frame);
this->valid = frame.valid; this->valid = frame.valid;
this->processed = frame.processed; if(!this->synonym) this->processed = frame.processed;
this->rssi = frame.rssi; this->rssi = frame.rssi;
this->lqi = frame.lqi; this->lqi = frame.lqi;
this->cmd = frame.cmd; this->cmd = frame.cmd;
@ -970,7 +992,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
int8_t dir = 0; int8_t dir = 0;
switch(frame.cmd) { switch(frame.cmd) {
case somfy_commands::My: case somfy_commands::My:
if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1) { if(this->shadeType != shade_types::drycontact && !this->isToggle()) {
digitalWrite(this->gpioUp, p_off); digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_off); digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioMy, p_on); digitalWrite(this->gpioMy, p_on);
@ -979,7 +1001,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
} }
break; break;
case somfy_commands::Up: case somfy_commands::Up:
if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioMy, p_off); digitalWrite(this->gpioMy, p_off);
digitalWrite(this->gpioDown, p_off); digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioUp, p_on); digitalWrite(this->gpioUp, p_on);
@ -989,7 +1011,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
break; break;
case somfy_commands::Toggle: case somfy_commands::Toggle:
case somfy_commands::Down: case somfy_commands::Down:
if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioMy, p_off); digitalWrite(this->gpioMy, p_off);
digitalWrite(this->gpioUp, p_off); digitalWrite(this->gpioUp, p_off);
} }
@ -998,7 +1020,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
Serial.printf("UP: false, DOWN: true, MY: false\n"); Serial.printf("UP: false, DOWN: true, MY: false\n");
break; break;
case somfy_commands::MyUp: case somfy_commands::MyUp:
if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioDown, p_off); digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioMy, p_on); digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioUp, p_on); digitalWrite(this->gpioUp, p_on);
@ -1006,7 +1028,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
} }
break; break;
case somfy_commands::MyDown: case somfy_commands::MyDown:
if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioUp, p_off); digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioMy, p_on); digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioDown, p_on); digitalWrite(this->gpioDown, p_on);
@ -1014,7 +1036,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
} }
break; break;
case somfy_commands::MyUpDown: case somfy_commands::MyUpDown:
if(this->shadeType != shade_types::drycontact && this->shadeType != shade_types::garage1 && this->shadeType != shade_types::drycontact2) { if(this->shadeType != shade_types::drycontact && this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioUp, p_on); digitalWrite(this->gpioUp, p_on);
digitalWrite(this->gpioMy, p_on); digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioDown, p_on); digitalWrite(this->gpioDown, p_on);
@ -1482,6 +1504,9 @@ void SomfyShade::publishDisco() {
case shade_types::lgate: case shade_types::lgate:
case shade_types::cgate: case shade_types::cgate:
case shade_types::rgate: case shade_types::rgate:
case shade_types::lgate1:
case shade_types::cgate1:
case shade_types::rgate1:
case shade_types::ldrapery: case shade_types::ldrapery:
case shade_types::rdrapery: case shade_types::rdrapery:
case shade_types::cdrapery: case shade_types::cdrapery:
@ -2370,7 +2395,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
break; break;
case somfy_commands::My: case somfy_commands::My:
if(this->shadeType == shade_types::drycontact2) return; if(this->shadeType == shade_types::drycontact2) return;
if(this->shadeType == shade_types::garage1) { if(this->isToggle()) { // This is a one button device
if(this->lastFrame.processed) return; if(this->lastFrame.processed) return;
this->lastFrame.processed = true; this->lastFrame.processed = true;
if(!this->isIdle()) this->p_target(this->currentPos); if(!this->isIdle()) this->p_target(this->currentPos);
@ -2398,6 +2423,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
this->lastFrame.await = curTime + 500; this->lastFrame.await = curTime + 500;
} }
else { else {
if(this->lastFrame.processed) return;
Serial.println("Moving to My target"); Serial.println("Moving to My target");
this->lastFrame.processed = true; this->lastFrame.processed = true;
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos); if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
@ -2406,6 +2432,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
} }
} }
else { else {
if(this->lastFrame.processed) return;
this->lastFrame.processed = true; this->lastFrame.processed = true;
if(!internal) { if(!internal) {
if(this->tiltType != tilt_types::tiltonly) this->p_target(this->currentPos); if(this->tiltType != tilt_types::tiltonly) this->p_target(this->currentPos);
@ -2511,8 +2538,10 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
this->lastFrame.processed = true; this->lastFrame.processed = true;
this->p_target(this->currentPos); this->p_target(this->currentPos);
this->p_tiltTarget(this->currentTiltPos); this->p_tiltTarget(this->currentTiltPos);
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
break; break;
case somfy_commands::Favorite: case somfy_commands::Favorite:
if(this->lastFrame.processed) return;
this->lastFrame.processed = true; this->lastFrame.processed = true;
if(this->simMy()) { if(this->simMy()) {
this->moveToMyPosition(); this->moveToMyPosition();
@ -2899,7 +2928,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
} }
} }
else if(cmd == somfy_commands::My) { else if(cmd == somfy_commands::My) {
if(this->shadeType == shade_types::garage1 || this->shadeType == shade_types::drycontact) if(this->isToggle() || this->shadeType == shade_types::drycontact)
SomfyRemote::sendCommand(cmd, repeat); SomfyRemote::sendCommand(cmd, repeat);
else if(this->shadeType == shade_types::drycontact2) return; else if(this->shadeType == shade_types::drycontact2) return;
else if(this->isIdle()) { else if(this->isIdle()) {
@ -2916,7 +2945,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
if(this->bitLength != 80) SomfyRemote::sendCommand(somfy_commands::My, repeat, stepSize); if(this->bitLength != 80) SomfyRemote::sendCommand(somfy_commands::My, repeat, stepSize);
else SomfyRemote::sendCommand(somfy_commands::Toggle, repeat); else SomfyRemote::sendCommand(somfy_commands::Toggle, repeat);
} }
else if(this->shadeType == shade_types::garage1 && cmd == somfy_commands::Prog) { else if(this->isToggle() && cmd == somfy_commands::Prog) {
SomfyRemote::sendCommand(somfy_commands::Toggle, repeat, stepSize); SomfyRemote::sendCommand(somfy_commands::Toggle, repeat, stepSize);
} }
else { else {
@ -2998,8 +3027,8 @@ void SomfyShade::moveToTiltTarget(float target) {
} }
void SomfyShade::moveToTarget(float pos, float tilt) { void SomfyShade::moveToTarget(float pos, float tilt) {
somfy_commands cmd = somfy_commands::My; somfy_commands cmd = somfy_commands::My;
if(this->shadeType == shade_types::garage1) { if(this->isToggle()) {
// Overload this as we cannot seek a position on a garage door. // Overload this as we cannot seek a position on a garage door or single button device.
this->p_target(pos); this->p_target(pos);
this->p_currentPos(pos); this->p_currentPos(pos);
this->emitState(); this->emitState();
@ -3081,12 +3110,24 @@ bool SomfyShade::save() {
} }
bool SomfyRoom::save() { somfy.commit(); return true; } bool SomfyRoom::save() { somfy.commit(); return true; }
bool SomfyGroup::save() { somfy.commit(); return true; } bool SomfyGroup::save() { somfy.commit(); return true; }
bool SomfyShade::isToggle() {
switch(this->shadeType) {
case shade_types::garage1:
case shade_types::lgate1:
case shade_types::cgate1:
case shade_types::rgate1:
return true;
default:
break;
}
return false;
}
bool SomfyShade::usesPin(uint8_t pin) { bool SomfyShade::usesPin(uint8_t pin) {
if(this->proto != radio_proto::GP_Remote && this->proto != radio_proto::GP_Relay) return false; if(this->proto != radio_proto::GP_Remote && this->proto != radio_proto::GP_Relay) return false;
if(this->gpioDown == pin) return true; if(this->gpioDown == pin) return true;
else if(this->shadeType == shade_types::drycontact) else if(this->shadeType == shade_types::drycontact)
return this->gpioDown == pin; return this->gpioDown == pin;
else if(this->shadeType == shade_types::garage1) { else if(this->isToggle()) {
if(this->proto == radio_proto::GP_Relay && this->gpioUp == pin) return true; if(this->proto == radio_proto::GP_Relay && this->gpioUp == pin) return true;
} }
else if(this->shadeType == shade_types::drycontact2) { else if(this->shadeType == shade_types::drycontact2) {
@ -3138,7 +3179,9 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) {
uint8_t upPin = obj.containsKey("gpioUp") ? obj["gpioUp"].as<uint8_t>() : this->gpioUp; uint8_t upPin = obj.containsKey("gpioUp") ? obj["gpioUp"].as<uint8_t>() : this->gpioUp;
uint8_t downPin = obj.containsKey("gpioDown") ? obj["gpioDown"].as<uint8_t>() : this->gpioDown; uint8_t downPin = obj.containsKey("gpioDown") ? obj["gpioDown"].as<uint8_t>() : this->gpioDown;
uint8_t myPin = obj.containsKey("gpioMy") ? obj["gpioMy"].as<uint8_t>() : this->gpioMy; uint8_t myPin = obj.containsKey("gpioMy") ? obj["gpioMy"].as<uint8_t>() : this->gpioMy;
if(type == shade_types::drycontact || (type == shade_types::garage1 && proto == radio_proto::GP_Remote)) upPin = myPin = 255; if(type == shade_types::drycontact ||
((type == shade_types::garage1 || type == shade_types::lgate1 || type == shade_types::cgate1 || type == shade_types::rgate1)
&& proto == radio_proto::GP_Remote)) upPin = myPin = 255;
else if(type == shade_types::drycontact2) myPin = 255; else if(type == shade_types::drycontact2) myPin = 255;
if(proto == radio_proto::GP_Relay) myPin = 255; if(proto == radio_proto::GP_Relay) myPin = 255;
if(somfy.transceiver.config.enabled) { if(somfy.transceiver.config.enabled) {
@ -3147,7 +3190,7 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) {
(myPin != 255 && somfy.transceiver.usesPin(myPin))) (myPin != 255 && somfy.transceiver.usesPin(myPin)))
ret = -10; ret = -10;
} }
if(settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref) { if(settings.connType == conn_types_t::ethernet || settings.connType == conn_types_t::ethernetpref) {
if((upPin != 255 && settings.Ethernet.usesPin(upPin)) || if((upPin != 255 && settings.Ethernet.usesPin(upPin)) ||
(downPin != 255 && somfy.transceiver.usesPin(downPin)) || (downPin != 255 && somfy.transceiver.usesPin(downPin)) ||
(myPin != 255 && somfy.transceiver.usesPin(myPin))) (myPin != 255 && somfy.transceiver.usesPin(myPin)))
@ -3895,6 +3938,7 @@ void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSi
this->lastFrame.repeats = repeat; this->lastFrame.repeats = repeat;
this->lastFrame.bitLength = this->bitLength; this->lastFrame.bitLength = this->bitLength;
this->lastFrame.stepSize = stepSize; this->lastFrame.stepSize = stepSize;
this->lastFrame.valid = true;
// Match the encKey to the rolling code. These keys range from 160 to 175. // Match the encKey to the rolling code. These keys range from 160 to 175.
this->lastFrame.encKey = 0xA0 | static_cast<uint8_t>(this->lastFrame.rollingCode & 0x000F); this->lastFrame.encKey = 0xA0 | static_cast<uint8_t>(this->lastFrame.rollingCode & 0x000F);
this->lastFrame.proto = this->proto; this->lastFrame.proto = this->proto;
@ -3953,9 +3997,13 @@ void SomfyRemote::repeatFrame(uint8_t repeat) {
somfy.transceiver.beginTransmit(); somfy.transceiver.beginTransmit();
byte frm[10]; byte frm[10];
this->lastFrame.encodeFrame(frm); this->lastFrame.encodeFrame(frm);
this->lastFrame.repeats++;
somfy.transceiver.sendFrame(frm, this->bitLength == 56 ? 2 : 12, this->bitLength); somfy.transceiver.sendFrame(frm, this->bitLength == 56 ? 2 : 12, this->bitLength);
for(uint8_t i = 0; i < repeat; i++) { for(uint8_t i = 0; i < repeat; i++) {
this->lastFrame.repeats++;
if(this->lastFrame.bitLength == 80) this->lastFrame.encode80BitFrame(&frm[0], this->lastFrame.repeats);
somfy.transceiver.sendFrame(frm, this->bitLength == 56 ? 7 : 6, this->bitLength); somfy.transceiver.sendFrame(frm, this->bitLength == 56 ? 7 : 6, this->bitLength);
esp_task_wdt_reset();
} }
somfy.transceiver.endTransmit(); somfy.transceiver.endTransmit();
//somfy.processFrame(this->lastFrame, true); //somfy.processFrame(this->lastFrame, true);
@ -4149,7 +4197,7 @@ void SomfyShadeController::toJSONGroups(JsonResponse &json) {
} }
void SomfyShadeController::toJSONRepeaters(JsonResponse &json) { void SomfyShadeController::toJSONRepeaters(JsonResponse &json) {
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) { for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
if(somfy.repeaters[i] != 0) json.addElem((uint8_t)somfy.repeaters[i]); if(somfy.repeaters[i] != 0) json.addElem((uint32_t)somfy.repeaters[i]);
} }
} }
void SomfyShadeController::loop() { void SomfyShadeController::loop() {
@ -4613,6 +4661,7 @@ void Transceiver::clearReceived(void) {
//packet_received = false; //packet_received = false;
//memset(receive_buffer, 0x00, sizeof(receive_buffer)); //memset(receive_buffer, 0x00, sizeof(receive_buffer));
if(this->config.enabled) if(this->config.enabled)
//attachInterrupt(interruptPin, handleReceive, FALLING);
attachInterrupt(interruptPin, handleReceive, CHANGE); attachInterrupt(interruptPin, handleReceive, CHANGE);
} }
void Transceiver::enableReceive(void) { void Transceiver::enableReceive(void) {
@ -4623,6 +4672,7 @@ void Transceiver::enableReceive(void) {
pinMode(this->config.RXPin, INPUT); pinMode(this->config.RXPin, INPUT);
interruptPin = digitalPinToInterrupt(this->config.RXPin); interruptPin = digitalPinToInterrupt(this->config.RXPin);
ELECHOUSE_cc1101.SetRx(); ELECHOUSE_cc1101.SetRx();
//attachInterrupt(interruptPin, handleReceive, FALLING);
attachInterrupt(interruptPin, handleReceive, CHANGE); attachInterrupt(interruptPin, handleReceive, CHANGE);
Serial.printf("Enabled receive on Pin #%d Timing: %ld\n", this->config.RXPin, millis() - timing); Serial.printf("Enabled receive on Pin #%d Timing: %ld\n", this->config.RXPin, millis() - timing);
} }

View file

@ -67,7 +67,10 @@ enum class shade_types : byte {
drycontact2 = 0x0A, drycontact2 = 0x0A,
lgate = 0x0B, lgate = 0x0B,
cgate = 0x0C, cgate = 0x0C,
rgate = 0x0D rgate = 0x0D,
lgate1 = 0x0E,
cgate1 = 0x0F,
rgate1 = 0x10
}; };
enum class tilt_types : byte { enum class tilt_types : byte {
none = 0x00, none = 0x00,
@ -171,6 +174,7 @@ struct somfy_relay_t {
struct somfy_frame_t { struct somfy_frame_t {
bool valid = false; bool valid = false;
bool processed = false; bool processed = false;
bool synonym = false;
radio_proto proto = radio_proto::RTS; radio_proto proto = radio_proto::RTS;
int rssi = 0; int rssi = 0;
byte lqi = 0x0; byte lqi = 0x0;
@ -188,10 +192,12 @@ struct somfy_frame_t {
void print(); void print();
void encode80BitFrame(byte *frame, uint8_t repeat); void encode80BitFrame(byte *frame, uint8_t repeat);
byte calc80Checksum(byte b0, byte b1, byte b2); byte calc80Checksum(byte b0, byte b1, byte b2);
byte encode80Byte7(byte start, uint8_t repeat);
void encodeFrame(byte *frame); void encodeFrame(byte *frame);
void decodeFrame(byte* frame); void decodeFrame(byte* frame);
void decodeFrame(somfy_rx_t *rx); void decodeFrame(somfy_rx_t *rx);
bool isRepeat(somfy_frame_t &f); bool isRepeat(somfy_frame_t &f);
bool isSynonym(somfy_frame_t &f);
void copy(somfy_frame_t &f); void copy(somfy_frame_t &f);
}; };
@ -319,6 +325,7 @@ class SomfyShade : public SomfyRemote {
void setMovement(int8_t dir); void setMovement(int8_t dir);
void setTarget(float target); void setTarget(float target);
bool isAtTarget(); bool isAtTarget();
bool isToggle();
void moveToTarget(float pos, float tilt = -1.0f); void moveToTarget(float pos, float tilt = -1.0f);
void moveToTiltTarget(float target); void moveToTiltTarget(float target);
void sendTiltCommand(somfy_commands cmd); void sendTiltCommand(somfy_commands cmd);

View file

@ -49,23 +49,33 @@ void loop() {
Serial.print("Rebooting after "); Serial.print("Rebooting after ");
Serial.print(rebootDelay.rebootTime); Serial.print(rebootDelay.rebootTime);
Serial.println("ms"); Serial.println("ms");
net.end();
ESP.restart(); ESP.restart();
return;
} }
uint32_t timing = millis(); uint32_t timing = millis();
net.loop(); net.loop();
if(millis() - timing > 100) Serial.printf("Timing Net: %ldms\n", millis() - timing); if(millis() - timing > 100) Serial.printf("Timing Net: %ldms\n", millis() - timing);
timing = millis(); timing = millis();
esp_task_wdt_reset();
somfy.loop(); somfy.loop();
if(millis() - timing > 100) Serial.printf("Timing Somfy: %ldms\n", millis() - timing); if(millis() - timing > 100) Serial.printf("Timing Somfy: %ldms\n", millis() - timing);
timing = millis(); timing = millis();
if(net.connected()) { esp_task_wdt_reset();
if(!rebootDelay.reboot) git.loop(); if(net.connected() || net.softAPOpened) {
if(!rebootDelay.reboot && net.connected() && !net.softAPOpened) {
git.loop();
esp_task_wdt_reset();
}
webServer.loop(); webServer.loop();
if(millis() - timing > 200) Serial.printf("Timing WebServer: %ldms\n", millis() - timing); esp_task_wdt_reset();
if(millis() - timing > 100) Serial.printf("Timing WebServer: %ldms\n", millis() - timing);
esp_task_wdt_reset();
timing = millis(); timing = millis();
sockEmit.loop(); sockEmit.loop();
if(millis() - timing > 100) Serial.printf("Timing Socket: %ldms\n", millis() - timing); if(millis() - timing > 100) Serial.printf("Timing Socket: %ldms\n", millis() - timing);
esp_task_wdt_reset();
timing = millis(); timing = millis();
} }
if(rebootDelay.reboot && millis() > rebootDelay.rebootTime) { if(rebootDelay.reboot && millis() > rebootDelay.rebootTime) {
@ -73,14 +83,4 @@ void loop() {
ESP.restart(); ESP.restart();
} }
esp_task_wdt_reset(); esp_task_wdt_reset();
/*
if(heap < oldheap) {
Serial.print("Heap: ");
Serial.print(oldheap);
Serial.print(" -> ");
Serial.println(heap);
}
oldheap = heap;
*/
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -9,6 +9,7 @@
[[maybe_unused]] static void SETCHARPROP(char *prop, const char *value, size_t size) {strncpy(prop, value, size); prop[size - 1] = '\0';} [[maybe_unused]] static void SETCHARPROP(char *prop, const char *value, size_t size) {strncpy(prop, value, size); prop[size - 1] = '\0';}
/*
namespace util { namespace util {
// Createa a custom to_string function. C++ can be annoying // Createa a custom to_string function. C++ can be annoying
// with all the trailing 0s on number formats. // with all the trailing 0s on number formats.
@ -23,6 +24,8 @@ namespace util {
return str; return str;
} }
} }
*/
static void _ltrim(char *str) { static void _ltrim(char *str) {
int s = 0, j, k = 0; int s = 0, j, k = 0;
int e = strlen(str); int e = strlen(str);

View file

@ -21,7 +21,7 @@ void JsonSockEvent::endEvent(uint8_t num) {
else this->server->sendTXT(num, this->buff); else this->server->sendTXT(num, this->buff);
} }
void JsonSockEvent::_safecat(const char *val, bool escape) { void JsonSockEvent::_safecat(const char *val, bool escape) {
size_t len = strlen(val) + strlen(this->buff); size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
if(escape) len += 2; if(escape) len += 2;
if(len >= this->buffSize) { if(len >= this->buffSize) {
Serial.printf("Socket exceeded buffer size %d - %d\n", this->buffSize, len); Serial.printf("Socket exceeded buffer size %d - %d\n", this->buffSize, len);
@ -29,7 +29,8 @@ void JsonSockEvent::_safecat(const char *val, bool escape) {
return; return;
} }
if(escape) strcat(this->buff, "\""); if(escape) strcat(this->buff, "\"");
strcat(this->buff, val); if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]);
else strcat(this->buff, val);
if(escape) strcat(this->buff, "\""); if(escape) strcat(this->buff, "\"");
} }
void JsonResponse::beginResponse(WebServer *server, char *buff, size_t buffSize) { void JsonResponse::beginResponse(WebServer *server, char *buff, size_t buffSize) {
@ -52,13 +53,14 @@ void JsonResponse::send() {
this->_headersSent = true; this->_headersSent = true;
} }
void JsonResponse::_safecat(const char *val, bool escape) { void JsonResponse::_safecat(const char *val, bool escape) {
size_t len = strlen(val) + strlen(this->buff); size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
if(escape) len += 2; if(escape) len += 2;
if(len >= this->buffSize) { if(len >= this->buffSize) {
this->send(); this->send();
} }
if(escape) strcat(this->buff, "\""); if(escape) strcat(this->buff, "\"");
strcat(this->buff, val); if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]);
else strcat(this->buff, val);
if(escape) strcat(this->buff, "\""); if(escape) strcat(this->buff, "\"");
} }
@ -133,13 +135,70 @@ void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_nu
void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); } void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); }
void JsonFormatter::_safecat(const char *val, bool escape) { void JsonFormatter::_safecat(const char *val, bool escape) {
size_t len = strlen(val) + strlen(this->buff); size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
if(escape) len += 2; if(escape) len += 2;
if(len >= this->buffSize) { if(len >= this->buffSize) {
return; return;
} }
if(escape) strcat(this->buff, "\""); if(escape) strcat(this->buff, "\"");
strcat(this->buff, val); if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]);
else strcat(this->buff, val);
if(escape) strcat(this->buff, "\""); if(escape) strcat(this->buff, "\"");
} }
void JsonFormatter::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); } void JsonFormatter::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); }
uint32_t JsonFormatter::calcEscapedLength(const char *raw) {
uint32_t len = 0;
for(size_t i = strlen(raw); i > 0; i--) {
switch(raw[i]) {
case '"':
case '/':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\\':
len += 2;
break;
default:
len++;
break;
}
}
return len;
}
void JsonFormatter::escapeString(const char *raw, char *escaped) {
for(uint32_t i = 0; i < strlen(raw); i++) {
switch(raw[i]) {
case '"':
strcat(escaped, "\\\"");
break;
case '/':
strcat(escaped, "\\/");
break;
case '\b':
strcat(escaped, "\\b");
break;
case '\f':
strcat(escaped, "\\f");
break;
case '\n':
strcat(escaped, "\\n");
break;
case '\r':
strcat(escaped, "\\r");
break;
case '\t':
strcat(escaped, "\\t");
break;
case '\\':
strcat(escaped, "\\\\");
break;
default:
size_t len = strlen(escaped);
escaped[len] = raw[i];
escaped[len+1] = 0x00;
break;
}
}
}

View file

@ -16,6 +16,8 @@ class JsonFormatter {
virtual void _safecat(const char *val, bool escape = false); virtual void _safecat(const char *val, bool escape = false);
void _appendNumber(const char *name); void _appendNumber(const char *name);
public: public:
void escapeString(const char *raw, char *escaped);
uint32_t calcEscapedLength(const char *raw);
void beginObject(const char *name = nullptr); void beginObject(const char *name = nullptr);
void endObject(); void endObject();
void beginArray(const char *name = nullptr); void beginArray(const char *name = nullptr);

46
Web.cpp
View file

@ -215,7 +215,7 @@ void Web::handleStreamFile(WebServer &server, const char *filename, const char *
} }
webServer.sendCORSHeaders(server); webServer.sendCORSHeaders(server);
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
esp_task_wdt_reset();
// Load the index html page from the data directory. // Load the index html page from the data directory.
Serial.print("Loading file "); Serial.print("Loading file ");
Serial.println(filename); Serial.println(filename);
@ -225,9 +225,11 @@ void Web::handleStreamFile(WebServer &server, const char *filename, const char *
Serial.println(filename); Serial.println(filename);
server.send(500, _encoding_text, "Error opening file"); server.send(500, _encoding_text, "Error opening file");
} }
esp_task_wdt_reset(); esp_task_wdt_delete(NULL);
server.streamFile(file, encoding); server.streamFile(file, encoding);
file.close(); file.close();
esp_task_wdt_add(NULL);
esp_task_wdt_reset();
} }
void Web::handleController(WebServer &server) { void Web::handleController(WebServer &server) {
webServer.sendCORSHeaders(server); webServer.sendCORSHeaders(server);
@ -789,8 +791,8 @@ void Web::handleDiscovery(WebServer &server) {
if (method == HTTP_POST || method == HTTP_GET) { if (method == HTTP_POST || method == HTTP_GET) {
Serial.println("Discovery Requested"); Serial.println("Discovery Requested");
char connType[10] = "Unknown"; char connType[10] = "Unknown";
if(net.connType == conn_types::ethernet) strcpy(connType, "Ethernet"); if(net.connType == conn_types_t::ethernet) strcpy(connType, "Ethernet");
else if(net.connType == conn_types::wifi) strcpy(connType, "Wifi"); else if(net.connType == conn_types_t::wifi) strcpy(connType, "Wifi");
JsonResponse resp; JsonResponse resp;
resp.beginResponse(&server, g_content, sizeof(g_content)); resp.beginResponse(&server, g_content, sizeof(g_content));
@ -2169,7 +2171,8 @@ void Web::begin() {
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
esp_task_wdt_delete(NULL); esp_task_wdt_delete(NULL);
int n = WiFi.scanNetworks(); if(net.softAPOpened) WiFi.disconnect(false);
int n = WiFi.scanNetworks(false, true);
esp_task_wdt_add(NULL); esp_task_wdt_add(NULL);
Serial.print("Scanned "); Serial.print("Scanned ");
@ -2371,25 +2374,32 @@ void Web::begin() {
// Parse out all the inputs. // Parse out all the inputs.
bool reboot = false; bool reboot = false;
if(obj.containsKey("connType") && obj["connType"].as<uint8_t>() != static_cast<uint8_t>(settings.connType)) { if(obj.containsKey("connType") && obj["connType"].as<uint8_t>() != static_cast<uint8_t>(settings.connType)) {
settings.connType = static_cast<conn_types>(obj["connType"].as<uint8_t>()); settings.connType = static_cast<conn_types_t>(obj["connType"].as<uint8_t>());
settings.save(); settings.save();
reboot = true; reboot = true;
} }
if(settings.connType == conn_types::wifi) { if(obj.containsKey("wifi")) {
if(obj.containsKey("ssid") && obj["ssid"].as<String>().compareTo(settings.WIFI.ssid) != 0) reboot = true; JsonObject objWifi = obj["wifi"];
if(obj.containsKey("passphrase") && obj["passphrase"].as<String>().compareTo(settings.WIFI.passphrase) != 0) reboot = true; if(settings.connType == conn_types_t::wifi) {
if(objWifi.containsKey("ssid") && objWifi["ssid"].as<String>().compareTo(settings.WIFI.ssid) != 0) {
if(WiFi.softAPgetStationNum() == 0) reboot = true;
}
if(objWifi.containsKey("passphrase") && objWifi["passphrase"].as<String>().compareTo(settings.WIFI.passphrase) != 0) {
if(WiFi.softAPgetStationNum() == 0) reboot = true;
}
}
settings.WIFI.fromJSON(objWifi);
settings.WIFI.save();
} }
else { if(obj.containsKey("ethernet"))
{
JsonObject objEth = obj["ethernet"];
// This is an ethernet connection so if anything changes we need to reboot. // This is an ethernet connection so if anything changes we need to reboot.
reboot = true; if(settings.connType == conn_types_t::ethernet || settings.connType == conn_types_t::ethernetpref)
reboot = true;
settings.Ethernet.fromJSON(objEth);
settings.Ethernet.save();
} }
JsonObject objWifi = obj["wifi"];
JsonObject objEth = obj["ethernet"];
settings.WIFI.fromJSON(objWifi);
settings.Ethernet.fromJSON(objEth);
settings.WIFI.save();
settings.Ethernet.save();
if (reboot) { if (reboot) {
Serial.println("Rebooting ESP for new Network settings..."); Serial.println("Rebooting ESP for new Network settings...");
rebootDelay.reboot = true; rebootDelay.reboot = true;

View file

@ -1 +1 @@
2.4.3 2.4.7

View file

@ -8,9 +8,9 @@
<meta name="apple-mobile-web-app-title" content="ESPSomfy RTS App"> <meta name="apple-mobile-web-app-title" content="ESPSomfy RTS App">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="stylesheet" href="main.css?v=2.4.3c" type="text/css" /> <link rel="stylesheet" href="main.css?v=2.4.7c" type="text/css" />
<link rel="stylesheet" href="widgets.css?v=2.4.3c" type="text/css" /> <link rel="stylesheet" href="widgets.css?v=2.4.7c" type="text/css" />
<link rel="stylesheet" href="icons.css?v=2.4.3c" type="text/css" /> <link rel="stylesheet" href="icons.css?v=2.4.7c" type="text/css" />
<link rel="icon" type="image/png" href="favicon.png" /> <link rel="icon" type="image/png" href="favicon.png" />
<!-- iPad retina icon --> <!-- iPad retina icon -->
@ -114,7 +114,7 @@
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<script type="text/javascript" src="index.js?v=2.4.3c"></script> <script type="text/javascript" src="index.js?v=2.4.7c"></script>
</head> </head>
<body> <body>
<div id="divContainer" class="container main" data-auth="false"> <div id="divContainer" class="container main" data-auth="false">
@ -160,7 +160,7 @@
<input id="cbSsdpBroadcast" name="ssdpBroadcast" type="checkbox" data-bind="general.ssdpBroadcast" style="display:inline-block;" /> <input id="cbSsdpBroadcast" name="ssdpBroadcast" type="checkbox" data-bind="general.ssdpBroadcast" style="display:inline-block;" />
<label for="cbSsdpBroadcast" style="display:inline-block;cursor:pointer;">Broadcast uPnP over SSDP</label> <label for="cbSsdpBroadcast" style="display:inline-block;cursor:pointer;">Broadcast uPnP over SSDP</label>
</div> </div>
<div class="field-group"> <div class="field-group" style="margin-top:-12px;">
<input id="cbCheckForUpdate" type="checkbox" data-bind="general.checkForUpdate" style="display:inline-block;" /> <input id="cbCheckForUpdate" type="checkbox" data-bind="general.checkForUpdate" style="display:inline-block;" />
<label for="cbCheckForUpdate" style="display:inline-block;cursor:pointer;">Auto Check for Updates</label> <label for="cbCheckForUpdate" style="display:inline-block;cursor:pointer;">Auto Check for Updates</label>
</div> </div>
@ -275,18 +275,25 @@
<div id="divNetworkSettings" style="display:none;"> <div id="divNetworkSettings" style="display:none;">
<div class="subtab-container"><span class="selected" data-grpid="divNetAdapter">Adapter</span><span data-grpid="divDHCP">DHCP/Static IP</span><span data-grpid="divMQTT">MQTT</span></div> <div class="subtab-container"><span class="selected" data-grpid="divNetAdapter">Adapter</span><span data-grpid="divDHCP">DHCP/Static IP</span><span data-grpid="divMQTT">MQTT</span></div>
<div id="divNetAdapter" class="subtab-content"> <div id="divNetAdapter" class="subtab-content">
<div class="field-group" style="vertical-align:middle;color:#00bcd4;margin-top:-24px;margin-bottom:18px;"> <div class="field-group" style="vertical-align:middle;color:#00bcd4;margin-top:-24px;">
<input id="cbHardwired" name="hardwired" data-bind="ethernet.hardwired" type="checkbox" style="display:inline-block;" onclick="wifi.useEthernetClicked();" /> <input id="cbHardwired" name="hardwired" data-bind="ethernet.hardwired" type="checkbox" style="display:inline-block;" onclick="wifi.useEthernetClicked();" />
<label for="cbHardwired" style="display:inline-block;cursor:pointer;">Use Ethernet</label> <label for="cbHardwired" style="display:inline-block;cursor:pointer;">Use Ethernet</label>
<div id="divRoaming" style="display:inline-block;padding-left:7px;">
<input id="cbRoaming" name="roaming" data-bind="wifi.roaming" type="checkbox" style="display:inline-block;" />
<label for="cbRoaming" style="display:inline-block;cursor:pointer;">Enable Roaming</label>
</div>
<div id="divFallbackWireless" style="display:inline-block;padding-left:7px;"> <div id="divFallbackWireless" style="display:inline-block;padding-left:7px;">
<input id="cbFallbackWireless" name="fallbackwireless" data-bind="ethernet.wirelessFallback" type="checkbox" style="display:inline-block;" /> <input id="cbFallbackWireless" name="fallbackwireless" data-bind="ethernet.wirelessFallback" type="checkbox" style="display:inline-block;" />
<label for="cbFallbackWireless" style="display:inline-block;cursor:pointer;">Fallback to Wireless</label> <label for="cbFallbackWireless" style="display:inline-block;cursor:pointer;">Fallback to Wireless</label>
</div> </div>
</div> </div>
<div class="field-group" style="vertical-align:middle;color:#00bcd4;margin-top:-12px;margin-bottom:18px;">
<div id="divHiddenSSID" style="display:inline-block;">
<input id="cbHiddenSSID" data-bind="wifi.hidden" type="checkbox" style="display:inline-block;" onclick="wifi.hiddenSSIDClicked();" />
<label for="cbHiddenSSID" style="display:inline-block;cursor:pointer;">Use Hidden SSID</label>
</div>
<div id="divRoaming" style="display:inline-block;padding-left:7px;">
<input id="cbRoaming" name="roaming" data-bind="wifi.roaming" type="checkbox" style="display:inline-block;" />
<label for="cbRoaming" style="display:inline-block;cursor:pointer;">Enable Roaming</label>
</div>
</div>
<div id="divWiFiMode"> <div id="divWiFiMode">
<form method="post" action="/scan"> <form method="post" action="/scan">
<div id="divAps" data-lastloaded="0" style="border-radius:5px;border:solid 1px #00bcd4;margin-bottom:-10px;"></div> <div id="divAps" data-lastloaded="0" style="border-radius:5px;border:solid 1px #00bcd4;margin-bottom:-10px;"></div>
@ -517,6 +524,10 @@
<option value="11">Gate (left)</option> <option value="11">Gate (left)</option>
<option value="12">Gate (center)</option> <option value="12">Gate (center)</option>
<option value="13">Gate (right)</option> <option value="13">Gate (right)</option>
<option value="14">Gate (1-button left)</option>
<option value="15">Gate (1-button center)</option>
<option value="16">Gate (1-button right)</option>
</select> </select>
<label for="selShadeType">Type</label> <label for="selShadeType">Type</label>
</div> </div>

View file

@ -1270,7 +1270,7 @@ var security = new Security();
class General { class General {
initialized = false; initialized = false;
appVersion = 'v2.4.3'; appVersion = 'v2.4.7';
reloadApp = false; reloadApp = false;
init() { init() {
if (this.initialized) return; if (this.initialized) return;
@ -1603,12 +1603,13 @@ var general = new General();
class Wifi { class Wifi {
initialized = false; initialized = false;
ethBoardTypes = [{ val: 0, label: 'Custom Config' }, ethBoardTypes = [{ val: 0, label: 'Custom Config' },
{ val: 1, label: 'WT32-ETH01', clk: 0, ct: 0, addr: 1, pwr: 16, mdc: 23, mdio: 18 }, { val: 7, label: 'EST-PoE-32 - Everything Smart', clk: 3, ct: 0, addr: 0, pwr: 12, mdc: 23, mdio: 18 },
{ val: 2, label: 'Olimex ESP32-POE', clk: 3, ct: 0, addr: 0, pwr: 12, mdc: 23, mdio: 18 }, { val: 3, label: 'ESP32-EVB - Olimex', clk: 0, ct: 0, addr: 0, pwr: -1, mdc: 23, mdio: 18 },
{ val: 3, label: 'Olimex ESP32-EVB', clk: 0, ct: 0, addr: 0, pwr: -1, mdc: 23, mdio: 18 }, { val: 2, label: 'ESP32-POE - Olimex', clk: 3, ct: 0, addr: 0, pwr: 12, mdc: 23, mdio: 18 },
{ val: 4, label: 'LILYGO T-Internet POE', clk: 3, ct: 0, addr: 0, pwr: 16, mdc: 23, mdio: 18 }, { val: 4, label: 'T-Internet POE - LILYGO', clk: 3, ct: 0, addr: 0, pwr: 16, mdc: 23, mdio: 18 },
{ val: 5, label: 'wESP32 v7+', clk: 0, ct: 2, addr: 0, pwr: -1, mdc: 16, mdio: 17 }, { val: 5, label: 'wESP32 v7+ - Silicognition', clk: 0, ct: 2, addr: 0, pwr: -1, mdc: 16, mdio: 17 },
{ val: 6, label: 'wESP32 < v7', clk: 0, ct: 0, addr: 0, pwr: -1, mdc: 16, mdio: 17 } { val: 6, label: 'wESP32 < v7 - Silicognition', clk: 0, ct: 0, addr: 0, pwr: -1, mdc: 16, mdio: 17 },
{ val: 1, label: 'WT32-ETH01 - Wireless Tag', clk: 0, ct: 0, addr: 1, pwr: 16, mdc: 23, mdio: 18 }
]; ];
ethClockModes = [{ val: 0, label: 'GPIO0 IN' }, { val: 1, label: 'GPIO0 OUT' }, { val: 2, label: 'GPIO16 OUT' }, { val: 3, label: 'GPIO17 OUT' }]; ethClockModes = [{ val: 0, label: 'GPIO0 IN' }, { val: 1, label: 'GPIO0 OUT' }, { val: 2, label: 'GPIO16 OUT' }, { val: 3, label: 'GPIO17 OUT' }];
ethPhyTypes = [{ val: 0, label: 'LAN8720' }, { val: 1, label: 'TLK110' }, { val: 2, label: 'RTL8201' }, { val: 3, label: 'DP83848' }, { val: 4, label: 'DM9051' }, { val: 5, label: 'KZ8081' }]; ethPhyTypes = [{ val: 0, label: 'LAN8720' }, { val: 1, label: 'TLK110' }, { val: 2, label: 'RTL8201' }, { val: 3, label: 'DP83848' }, { val: 4, label: 'DM9051' }, { val: 5, label: 'KZ8081' }];
@ -1675,6 +1676,7 @@ class Wifi {
document.getElementById('cbHardwired').checked = settings.connType >= 2; document.getElementById('cbHardwired').checked = settings.connType >= 2;
document.getElementById('cbFallbackWireless').checked = settings.connType === 3; document.getElementById('cbFallbackWireless').checked = settings.connType === 3;
ui.toElement(pnl, settings); ui.toElement(pnl, settings);
/*
if (settings.connType >= 2) { if (settings.connType >= 2) {
document.getElementById('divWiFiMode').style.display = 'none'; document.getElementById('divWiFiMode').style.display = 'none';
document.getElementById('divEthernetMode').style.display = ''; document.getElementById('divEthernetMode').style.display = '';
@ -1687,10 +1689,13 @@ class Wifi {
document.getElementById('divFallbackWireless').style.display = 'none'; document.getElementById('divFallbackWireless').style.display = 'none';
document.getElementById('divRoaming').style.display = 'inline-block'; document.getElementById('divRoaming').style.display = 'inline-block';
} }
*/
ui.toElement(document.getElementById('divDHCP'), settings);
document.getElementById('divETHSettings').style.display = settings.ethernet.boardType === 0 ? '' : 'none'; document.getElementById('divETHSettings').style.display = settings.ethernet.boardType === 0 ? '' : 'none';
document.getElementById('divStaticIP').style.display = settings.ip.dhcp ? 'none' : ''; document.getElementById('divStaticIP').style.display = settings.ip.dhcp ? 'none' : '';
ui.toElement(document.getElementById('divDHCP'), settings);
document.getElementById('spanCurrentIP').innerHTML = settings.ip.ip; document.getElementById('spanCurrentIP').innerHTML = settings.ip.ip;
this.useEthernetClicked();
this.hiddenSSIDClicked();
} }
}); });
@ -1701,6 +1706,12 @@ class Wifi {
document.getElementById('divEthernetMode').style.display = useEthernet ? '' : 'none'; document.getElementById('divEthernetMode').style.display = useEthernet ? '' : 'none';
document.getElementById('divFallbackWireless').style.display = useEthernet ? 'inline-block' : 'none'; document.getElementById('divFallbackWireless').style.display = useEthernet ? 'inline-block' : 'none';
document.getElementById('divRoaming').style.display = useEthernet ? 'none' : 'inline-block'; document.getElementById('divRoaming').style.display = useEthernet ? 'none' : 'inline-block';
document.getElementById('divHiddenSSID').style.display = useEthernet ? 'none' : 'inline-block';
}
hiddenSSIDClicked() {
let hidden = document.getElementById('cbHiddenSSID').checked;
if (hidden) document.getElementById('cbRoaming').checked = false;
document.getElementById('cbRoaming').disabled = hidden;
} }
async loadAPs() { async loadAPs() {
if (document.getElementById('btnScanAPs').classList.contains('disabled')) return; if (document.getElementById('btnScanAPs').classList.contains('disabled')) return;
@ -1926,6 +1937,9 @@ class Somfy {
{ type: 11, name: 'Gate (left)', ico: 'icss-lgate', lift: true, fcmd: true, fpos: true }, { type: 11, name: 'Gate (left)', ico: 'icss-lgate', lift: true, fcmd: true, fpos: true },
{ type: 12, name: 'Gate (center)', ico: 'icss-cgate', lift: true, fcmd: true, fpos: true }, { type: 12, name: 'Gate (center)', ico: 'icss-cgate', lift: true, fcmd: true, fpos: true },
{ type: 13, name: 'Gate (right)', ico: 'icss-rgate', lift: true, fcmd: true, fpos: true }, { type: 13, name: 'Gate (right)', ico: 'icss-rgate', lift: true, fcmd: true, fpos: true },
{ type: 14, name: 'Gate (1-button left)', ico: 'icss-lgate', lift: true, fcmd: true, fpos: true },
{ type: 15, name: 'Gate (1-button center)', ico: 'icss-cgate', lift: true, fcmd: true, fpos: true },
{ type: 16, name: 'Gate (1-button right)', ico: 'icss-rgate', lift: true, fcmd: true, fpos: true },
]; ];
init() { init() {
if (this.initialized) return; if (this.initialized) return;
@ -2783,7 +2797,7 @@ class Somfy {
pinMaps = [ pinMaps = [
{ name: '', maxPins: 39, inputs: [0, 1, 6, 7, 8, 9, 10, 11, 37, 38], outputs: [3, 6, 7, 8, 9, 10, 11, 34, 35, 36, 37, 38, 39] }, { name: '', maxPins: 39, inputs: [0, 1, 6, 7, 8, 9, 10, 11, 37, 38], outputs: [3, 6, 7, 8, 9, 10, 11, 34, 35, 36, 37, 38, 39] },
{ name: 's2', maxPins: 46, inputs: [0, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 45], outputs: [0, 19, 20, 26, 27, 28, 29, 30, 31, 32, 45, 46]}, { name: 's2', maxPins: 46, inputs: [0, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 45], outputs: [0, 19, 20, 26, 27, 28, 29, 30, 31, 32, 45, 46]},
{ name: 's3', maxPins: 48, inputs: [0, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 39, 40, 41, 42, 43], outputs: [0, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 39, 40, 41, 42, 43] }, { name: 's3', maxPins: 48, inputs: [19, 20, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32], outputs: [19, 20, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32] },
{ name: 'c3', maxPins: 21, inputs: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], outputs: [11, 12, 13, 14, 15, 16, 17, 21] } { name: 'c3', maxPins: 21, inputs: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], outputs: [11, 12, 13, 14, 15, 16, 17, 21] }
]; ];
@ -3246,6 +3260,9 @@ class Somfy {
if (obj.proto === 8 || obj.proto === 9) { if (obj.proto === 8 || obj.proto === 9) {
switch (obj.shadeType) { switch (obj.shadeType) {
case 5: // Garage 1-button case 5: // Garage 1-button
case 14: // Gate left 1-button
case 15: // Gate center 1-button
case 16: // Gate right 1-button
case 10: // Two button dry contact case 10: // Two button dry contact
if (obj.proto !== 9 && obj.gpioUp === obj.gpioDown) { if (obj.proto !== 9 && obj.gpioUp === obj.gpioDown) {
ui.errorMessage(document.getElementById('divSomfySettings'), 'For GPIO controlled motors the up and down GPIO selections must be unique.'); ui.errorMessage(document.getElementById('divSomfySettings'), 'For GPIO controlled motors the up and down GPIO selections must be unique.');
@ -3685,12 +3702,36 @@ class Somfy {
html += '<li>If the shade does not jog, press the prog button again until the shade jogs.</li>'; html += '<li>If the shade does not jog, press the prog button again until the shade jogs.</li>';
html += '</ul>'; html += '</ul>';
html += `<div class="button-container">`; html += `<div class="button-container">`;
html += `<button id="btnSendUnpairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;" onclick="somfy.sendCommand(${shadeId}, 'prog', 1);">Prog</button>`; html += `<button id="btnSendUnpairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;">Prog</button>`;
html += `<button id="btnMarkPaired" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;" onclick="somfy.setPaired(${shadeId}, false);">Shade Unpaired</button>`; html += `<button id="btnMarkPaired" type="button" style="padding-left:20px;padding-right:20px;display:inline-block;" onclick="somfy.setPaired(${shadeId}, false);">Shade Unpaired</button>`;
html += `<button id="btnStopUnpairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block" onclick="document.getElementById('divPairing').remove();">Close</button>`; html += `<button id="btnStopUnpairing" type="button" style="padding-left:20px;padding-right:20px;display:inline-block" onclick="document.getElementById('divPairing').remove();">Close</button>`;
html += `</div>`; html += `</div>`;
div.innerHTML = html; div.innerHTML = html;
let fnRepeatProg = (err, shade) => {
if (this.btnTimer) {
clearTimeout(this.btnTimer);
this.btnTimer = null;
}
if (err) return;
if (mouseDown) {
somfy.sendCommandRepeat(shadeId, 'prog', null, fnRepeatProg);
}
}
document.getElementById('somfyShade').appendChild(div); document.getElementById('somfyShade').appendChild(div);
let btn = document.getElementById('btnSendUnpairing');
btn.addEventListener('mousedown', (event) => {
console.log(this);
console.log(event);
console.log('mousedown');
somfy.sendCommand(shadeId, 'prog', null, (err, shade) => { fnRepeatProg(err, shade); });
}, true);
btn.addEventListener('touchstart', (event) => {
console.log(this);
console.log(event);
console.log('touchstart');
somfy.sendCommand(shadeId, 'prog', null, (err, shade) => { fnRepeatProg(err, shade); });
}, true);
return div; return div;
} }
sendCommand(shadeId, command, repeat, cb) { sendCommand(shadeId, command, repeat, cb) {
@ -4140,6 +4181,9 @@ class Somfy {
case 5: case 5:
case 9: case 9:
case 10: case 10:
case 14:
case 15:
case 16:
return; return;
} }
let tiltType = parseInt(shade.getAttribute('data-tilt'), 10) || 0; let tiltType = parseInt(shade.getAttribute('data-tilt'), 10) || 0;

View file

@ -150,6 +150,9 @@
.shadectl-buttons div.button-outline { .shadectl-buttons div.button-outline {
display: inline-block; display: inline-block;
} }
.shadectl-buttons[data-shadetype="14"] > .cmd-button[data-cmd="sunflag"],
.shadectl-buttons[data-shadetype="15"] > .cmd-button[data-cmd="sunflag"],
.shadectl-buttons[data-shadetype="16"] > .cmd-button[data-cmd="sunflag"],
.shadectl-buttons[data-shadetype="6"] > .cmd-button[data-cmd="sunflag"], .shadectl-buttons[data-shadetype="6"] > .cmd-button[data-cmd="sunflag"],
.shadectl-buttons[data-shadetype="5"] > .cmd-button[data-cmd="sunflag"], .shadectl-buttons[data-shadetype="5"] > .cmd-button[data-cmd="sunflag"],
.shadectl-buttons[data-shadetype="9"] > .cmd-button[data-cmd="sunflag"], .shadectl-buttons[data-shadetype="9"] > .cmd-button[data-cmd="sunflag"],
@ -159,6 +162,15 @@
.shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="my"], .shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="my"],
.shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="up"], .shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="up"],
.shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="down"], .shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="down"],
.shadectl-buttons[data-shadetype="14"] > .button-outline[data-cmd="my"],
.shadectl-buttons[data-shadetype="14"] > .button-outline[data-cmd="up"],
.shadectl-buttons[data-shadetype="14"] > .button-outline[data-cmd="down"],
.shadectl-buttons[data-shadetype="15"] > .button-outline[data-cmd="my"],
.shadectl-buttons[data-shadetype="15"] > .button-outline[data-cmd="up"],
.shadectl-buttons[data-shadetype="15"] > .button-outline[data-cmd="down"],
.shadectl-buttons[data-shadetype="16"] > .button-outline[data-cmd="my"],
.shadectl-buttons[data-shadetype="16"] > .button-outline[data-cmd="up"],
.shadectl-buttons[data-shadetype="16"] > .button-outline[data-cmd="down"],
.shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="my"] { .shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="my"] {
display: none; display: none;
} }
@ -174,10 +186,13 @@
.shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="down"] i { .shadectl-buttons[data-shadetype="10"] > .button-outline[data-cmd="down"] i {
top:.3em; top:.3em;
} }
.shadectl-buttons:not([data-shadetype="5"]):not([data-shadetype="9"]) > .button-outline[data-cmd="toggle"] { .shadectl-buttons:not([data-shadetype="5"]):not([data-shadetype="9"]):not([data-shadetype="14"]):not([data-shadetype="15"]):not([data-shadetype="16"]) > .button-outline[data-cmd="toggle"] {
display: none; display: none;
} }
.somfyShadeCtl[data-shadetype="5"] .shadectl-mypos, .somfyShadeCtl[data-shadetype="5"] .shadectl-mypos,
.somfyShadeCtl[data-shadetype="14"] .shadectl-mypos,
.somfyShadeCtl[data-shadetype="15"] .shadectl-mypos,
.somfyShadeCtl[data-shadetype="16"] .shadectl-mypos,
.somfyShadeCtl[data-shadetype="9"] .shadectl-mypos, .somfyShadeCtl[data-shadetype="9"] .shadectl-mypos,
.somfyShadeCtl[data-tilt="3"] .shadectl-mypos .my-pos, .somfyShadeCtl[data-tilt="3"] .shadectl-mypos .my-pos,
.somfyShadeCtl:not([data-shadetype="1"]) .shadectl-mypos .my-pos-tilt, .somfyShadeCtl:not([data-shadetype="1"]) .shadectl-mypos .my-pos-tilt,
@ -239,12 +254,24 @@
#somfyShade[data-proto="8"] #divGPIOMy, #somfyShade[data-proto="8"] #divGPIOMy,
#somfyShade[data-bitlength="56"] #divStepSettings, #somfyShade[data-bitlength="56"] #divStepSettings,
#somfyShade[data-shadetype="5"] #divStepSettings, #somfyShade[data-shadetype="5"] #divStepSettings,
#somfyShade[data-shadetype="14"] #divStepSettings,
#somfyShade[data-shadetype="15"] #divStepSettings,
#somfyShade[data-shadetype="16"] #divStepSettings,
#somfyShade[data-shadetype="6"] #divStepSettings { #somfyShade[data-shadetype="6"] #divStepSettings {
display: none; display: none;
} }
#somfyShade[data-proto="9"][data-shadetype="5"] #divGPIOUp, #somfyShade[data-proto="9"][data-shadetype="5"] #divGPIOUp,
#somfyShade[data-proto="9"][data-shadetype="5"] #divGPIOMy, #somfyShade[data-proto="9"][data-shadetype="5"] #divGPIOMy,
#somfyShade[data-proto="8"][data-shadetype="5"] #divGPIOMy, #somfyShade[data-proto="8"][data-shadetype="5"] #divGPIOMy,
#somfyShade[data-proto="9"][data-shadetype="14"] #divGPIOUp,
#somfyShade[data-proto="9"][data-shadetype="14"] #divGPIOMy,
#somfyShade[data-proto="8"][data-shadetype="14"] #divGPIOMy,
#somfyShade[data-proto="9"][data-shadetype="15"] #divGPIOUp,
#somfyShade[data-proto="9"][data-shadetype="15"] #divGPIOMy,
#somfyShade[data-proto="8"][data-shadetype="15"] #divGPIOMy,
#somfyShade[data-proto="9"][data-shadetype="16"] #divGPIOUp,
#somfyShade[data-proto="9"][data-shadetype="16"] #divGPIOMy,
#somfyShade[data-proto="8"][data-shadetype="16"] #divGPIOMy,
#somfyShade[data-proto="9"][data-shadetype="9"] #divGPIOUp, #somfyShade[data-proto="9"][data-shadetype="9"] #divGPIOUp,
#somfyShade[data-proto="9"][data-shadetype="9"] #divGPIOMy, #somfyShade[data-proto="9"][data-shadetype="9"] #divGPIOMy,
#somfyShade[data-proto="8"][data-shadetype="9"] #divGPIOUp { #somfyShade[data-proto="8"][data-shadetype="9"] #divGPIOUp {