Tsury noise detection

This commit is contained in:
cjkas 2026-03-30 09:22:22 +02:00
parent 72bfd26443
commit 0dbb1da2bf
6 changed files with 189 additions and 5 deletions

View file

@ -913,6 +913,10 @@
<input id="cbEnableRadio" name="enableRadio" type="checkbox" data-bind="transceiver.config.enabled" style="display:inline-block;" /> <input id="cbEnableRadio" name="enableRadio" type="checkbox" data-bind="transceiver.config.enabled" style="display:inline-block;" />
<label for="cbEnableRadio" style="display:inline-block;cursor:pointer;">Enable Radio</label> <label for="cbEnableRadio" style="display:inline-block;cursor:pointer;">Enable Radio</label>
</div> </div>
<div class="field-group" style="vertical-align:middle;margin-left:7px;float:right;display:inline-block;width:auto;">
<input id="cbNoiseDetection" name="noiseDetection" type="checkbox" data-bind="transceiver.config.noiseDetection" style="display:inline-block;" title="When enabled, the RX interrupt is automatically disabled if more than 100 pulses are detected within 10 seconds (RF noise burst). The interrupt is re-enabled after a 100ms cooldown. Enable this if your radio is frequently disrupted by nearby RF interference." />
<label for="cbNoiseDetection" style="display:inline-block;cursor:pointer;" title="When enabled, the RX interrupt is automatically disabled if more than 100 pulses are detected within 10 seconds (RF noise burst). The interrupt is re-enabled after a 100ms cooldown. Enable this if your radio is frequently disrupted by nearby RF interference.">Noise Detection</label>
</div>
<div class="field-group" style="margin-top:-18px;"><label style="font-size:12px;">(default when adding new motors)</label></div> <div class="field-group" style="margin-top:-18px;"><label style="font-size:12px;">(default when adding new motors)</label></div>
<div class="field-group1" style="white-space:nowrap"> <div class="field-group1" style="white-space:nowrap">
<div class="field-group radioPins"> <div class="field-group radioPins">

View file

@ -1954,7 +1954,7 @@ class Somfy {
this.loadPins('input', document.getElementById('selTransRXPin')); this.loadPins('input', document.getElementById('selTransRXPin'));
//this.loadSomfy(); //this.loadSomfy();
ui.toElement(document.getElementById('divTransceiverSettings'), { ui.toElement(document.getElementById('divTransceiverSettings'), {
transceiver: { config: { proto: 0, SCKPin: 18, CSNPin: 5, MOSIPin: 23, MISOPin: 19, TXPin: 12, RXPin: 13, frequency: 433.42, rxBandwidth: 97.96, type: 56, deviation: 11.43, txPower: 10, enabled: false } } transceiver: { config: { proto: 0, SCKPin: 18, CSNPin: 5, MOSIPin: 23, MISOPin: 19, TXPin: 12, RXPin: 13, frequency: 433.42, rxBandwidth: 97.96, type: 56, deviation: 11.43, txPower: 10, enabled: false, noiseDetection: false } }
}); });
this.loadPins('out', document.getElementById('selShadeGPIOUp')); this.loadPins('out', document.getElementById('selShadeGPIOUp'));
this.loadPins('out', document.getElementById('selShadeGPIODown')); this.loadPins('out', document.getElementById('selShadeGPIODown'));

View file

@ -708,7 +708,8 @@ bool ShadeConfigFile::readTransRecord(transceiver_config_t &cfg) {
cfg.frequency = this->readFloat(cfg.frequency); cfg.frequency = this->readFloat(cfg.frequency);
cfg.rxBandwidth = this->readFloat(cfg.rxBandwidth); cfg.rxBandwidth = this->readFloat(cfg.rxBandwidth);
cfg.deviation = this->readFloat(cfg.deviation); cfg.deviation = this->readFloat(cfg.deviation);
cfg.txPower = this->readInt8(cfg.txPower); cfg.txPower = this->readInt8(cfg.txPower);
cfg.noiseDetection = this->readBool(false);
if(this->file.position() != startPos + this->header.transRecordSize) { if(this->file.position() != startPos + this->header.transRecordSize) {
ESP_LOGD(TAG, "Reading to end of transceiver record"); ESP_LOGD(TAG, "Reading to end of transceiver record");
this->seekChar(CFG_REC_END); this->seekChar(CFG_REC_END);
@ -1065,7 +1066,8 @@ bool ShadeConfigFile::writeTransRecord(transceiver_config_t &cfg) {
this->writeFloat(cfg.frequency, 3); this->writeFloat(cfg.frequency, 3);
this->writeFloat(cfg.rxBandwidth, 2); this->writeFloat(cfg.rxBandwidth, 2);
this->writeFloat(cfg.deviation, 2); this->writeFloat(cfg.deviation, 2);
this->writeInt8(cfg.txPower, CFG_REC_END); this->writeInt8(cfg.txPower);
this->writeBool(cfg.noiseDetection, CFG_REC_END);
return true; return true;
} }
bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); } bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); }

View file

@ -3,6 +3,7 @@
#include <SPI.h> #include <SPI.h>
#include <esp_task_wdt.h> #include <esp_task_wdt.h>
#include <esp_chip_info.h> #include <esp_chip_info.h>
#include "driver/gpio.h"
#include "esp_log.h" #include "esp_log.h"
#include "Utils.h" #include "Utils.h"
#include "ConfigSettings.h" #include "ConfigSettings.h"
@ -45,6 +46,9 @@ int sort_asc(const void *cmp1, const void *cmp2) {
} }
static int interruptPin = 0; static int interruptPin = 0;
static volatile bool noiseDetected = false;
static volatile unsigned long noiseStart = 0;
static volatile uint32_t noiseCount = 0;
static uint8_t bit_length = 56; static uint8_t bit_length = 56;
somfy_commands translateSomfyCommand(const String& string) { somfy_commands translateSomfyCommand(const String& string) {
if (string.equalsIgnoreCase("My")) return somfy_commands::My; if (string.equalsIgnoreCase("My")) return somfy_commands::My;
@ -4187,6 +4191,21 @@ void Transceiver::sendFrame(byte *frame, uint8_t sync, uint8_t bitLength) {
} }
} }
void RECEIVE_ATTR Transceiver::handleReceive() { void RECEIVE_ATTR Transceiver::handleReceive() {
if (noiseDetected) return;
if (somfy.transceiver.config.noiseDetection) {
unsigned long now = millis();
if (noiseStart == 0) noiseStart = now;
if (now - noiseStart >= 10000) {
noiseStart = now;
noiseCount = 0;
}
noiseCount++;
if (noiseCount > 100) {
gpio_intr_disable((gpio_num_t)interruptPin);
noiseDetected = true;
return;
}
}
static unsigned long last_time = 0; static unsigned long last_time = 0;
const long time = micros(); const long time = micros();
const unsigned int duration = time - last_time; const unsigned int duration = time - last_time;
@ -4358,7 +4377,7 @@ void Transceiver::processFrequencyScan(bool received) {
currRSSI = -100; currRSSI = -100;
} }
if(millis() - lastScan > 100 && somfy_rx.status == waiting_synchro) { if(millis() - lastScan > 100 && (somfy_rx.status == waiting_synchro || noiseDetected)) {
lastScan = millis(); lastScan = millis();
this->emitFrequencyScan(); this->emitFrequencyScan();
currFreq += 0.01f; currFreq += 0.01f;
@ -4538,6 +4557,7 @@ void transceiver_config_t::fromJSON(JsonObject& obj) {
if(!obj["enabled"].isNull()) this->enabled = obj["enabled"]; if(!obj["enabled"].isNull()) this->enabled = obj["enabled"];
if(!obj["txPower"].isNull()) this->txPower = obj["txPower"]; if(!obj["txPower"].isNull()) this->txPower = obj["txPower"];
if(!obj["proto"].isNull()) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>()); if(!obj["proto"].isNull()) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>());
if(!obj["noiseDetection"].isNull()) this->noiseDetection = obj["noiseDetection"];
/* /*
if (!obj["internalCCMode"].isNull()) this->internalCCMode = obj["internalCCMode"]; if (!obj["internalCCMode"].isNull()) this->internalCCMode = obj["internalCCMode"];
if (!obj["modulationMode"].isNull()) this->modulationMode = obj["modulationMode"]; if (!obj["modulationMode"].isNull()) this->modulationMode = obj["modulationMode"];
@ -4625,7 +4645,8 @@ void transceiver_config_t::save() {
pref.putBool("radioInit", true); pref.putBool("radioInit", true);
pref.putChar("txPower", this->txPower); pref.putChar("txPower", this->txPower);
pref.putChar("proto", static_cast<uint8_t>(this->proto)); pref.putChar("proto", static_cast<uint8_t>(this->proto));
pref.putBool("noiseDetect", this->noiseDetection);
/* /*
pref.putBool("internalCCMode", this->internalCCMode); pref.putBool("internalCCMode", this->internalCCMode);
pref.putUChar("modulationMode", this->modulationMode); pref.putUChar("modulationMode", this->modulationMode);
@ -4724,6 +4745,7 @@ void transceiver_config_t::load() {
this->txPower = pref.getChar("txPower", this->txPower); this->txPower = pref.getChar("txPower", this->txPower);
this->rxBandwidth = pref.getFloat("rxBandwidth", this->rxBandwidth); this->rxBandwidth = pref.getFloat("rxBandwidth", this->rxBandwidth);
this->proto = static_cast<radio_proto>(pref.getChar("proto", static_cast<uint8_t>(this->proto))); this->proto = static_cast<radio_proto>(pref.getChar("proto", static_cast<uint8_t>(this->proto)));
this->noiseDetection = pref.getBool("noiseDetect", false);
this->removeNVSKey("internalCCMode"); this->removeNVSKey("internalCCMode");
this->removeNVSKey("modulationMode"); this->removeNVSKey("modulationMode");
this->removeNVSKey("channel"); this->removeNVSKey("channel");
@ -4857,6 +4879,14 @@ bool Transceiver::begin() {
} }
void Transceiver::loop() { void Transceiver::loop() {
somfy_rx_t rx; somfy_rx_t rx;
if (noiseDetected && rxmode != 3 && this->config.noiseDetection) {
if (millis() - noiseStart > 100) {
gpio_intr_enable((gpio_num_t)interruptPin);
noiseDetected = false;
noiseStart = 0;
noiseCount = 0;
}
}
if(rxmode == 3) { if(rxmode == 3) {
if(this->receive(&rx)) if(this->receive(&rx))
this->processFrequencyScan(true); this->processFrequencyScan(true);

147
src/SomfyController.ino.cpp Normal file
View file

@ -0,0 +1,147 @@
# 1 "C:\\Users\\oem\\AppData\\Local\\Temp\\tmpahrx2jqr"
#include <Arduino.h>
# 1 "C:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src/SomfyController.ino"
#include "esp_log.h"
#include <WiFi.h>
#include <LittleFS.h>
#include <esp_task_wdt.h>
#include "ConfigSettings.h"
#include "ESPNetwork.h"
#include "Web.h"
#include "Sockets.h"
#include "Utils.h"
#include "Somfy.h"
#include "MQTT.h"
#include "GitOTA.h"
#include "esp_core_dump.h"
static const char *TAG = "Main";
ConfigSettings settings;
Web webServer;
SocketEmitter sockEmit;
ESPNetwork net;
rebootDelay_t rebootDelay;
SomfyShadeController somfy;
MQTTClass mqtt;
GitUpdater git;
uint32_t oldheap = 0;
void listDir(const char *dirname, uint8_t levels);
void setup();
void loop();
#line 28 "C:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src/SomfyController.ino"
void listDir(const char *dirname, uint8_t levels) {
ESP_LOGI(TAG, "Listing: %s", dirname);
File root = LittleFS.open(dirname);
if (!root || !root.isDirectory()) {
ESP_LOGE(TAG, "Failed to open directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
ESP_LOGI(TAG, " DIR : %s", file.name());
if (levels) listDir(file.path(), levels - 1);
} else {
ESP_LOGI(TAG, " FILE: %-30s %d bytes", file.name(), file.size());
}
file = root.openNextFile();
}
}
void setup() {
Serial.begin(115200);
ESP_LOGI(TAG, "Startup/Boot....");
esp_core_dump_summary_t summary;
if (esp_core_dump_get_summary(&summary) == ESP_OK) {
ESP_LOGW(TAG, "*** Previous crash coredump found ***");
ESP_LOGW(TAG, " Task: %s", summary.exc_task);
ESP_LOGW(TAG, " PC: 0x%08x", summary.exc_pc);
#ifdef CONFIG_IDF_TARGET_ARCH_XTENSA
ESP_LOGW(TAG, " Cause: %d", summary.ex_info.exc_cause);
char bt_buf[256] = {0};
int pos = 0;
for (int i = 0; i < summary.exc_bt_info.depth; i++) {
pos += snprintf(bt_buf + pos, sizeof(bt_buf) - pos, " 0x%08x", summary.exc_bt_info.bt[i]);
}
ESP_LOGW(TAG, " Backtrace:%s", bt_buf);
#elif CONFIG_IDF_TARGET_ARCH_RISCV
ESP_LOGW(TAG, " Cause: %d", summary.ex_info.mcause);
ESP_LOGW(TAG, " MTVAL: 0x%08x RA: 0x%08x SP: 0x%08x",
summary.ex_info.mtval, summary.ex_info.ra, summary.ex_info.sp);
#endif
}
ESP_LOGI(TAG, "Mounting File System...");
if (LittleFS.begin()) {
ESP_LOGI(TAG, "Total: %d bytes", LittleFS.totalBytes());
ESP_LOGI(TAG, "Used: %d bytes", LittleFS.usedBytes());
ESP_LOGI(TAG, "Free: %d bytes", LittleFS.totalBytes() - LittleFS.usedBytes());
listDir("/", 3);
} else {
ESP_LOGE(TAG, "LittleFS mount failed!");
}
if(LittleFS.begin()) ESP_LOGI(TAG, "File system mounted successfully");
else ESP_LOGE(TAG, "Error mounting file system");
settings.begin();
if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true);
delay(10);
webServer.startup();
webServer.begin();
delay(1000);
net.setup();
somfy.begin();
#if ESP_ARDUINO_VERSION_MAJOR >= 3
const esp_task_wdt_config_t wdt_config = { .timeout_ms = 15000, .idle_core_mask = 1, .trigger_panic = true };
esp_task_wdt_init(&wdt_config);
#else
esp_task_wdt_init(15, true);
#endif
esp_task_wdt_add(NULL);
}
void loop() {
if(rebootDelay.reboot && millis() > rebootDelay.rebootTime) {
ESP_LOGI(TAG, "Rebooting after %lums", rebootDelay.rebootTime);
net.end();
ESP.restart();
return;
}
uint32_t timing = millis();
net.loop();
if(millis() - timing > 100) ESP_LOGD(TAG, "Timing Net: %ldms", millis() - timing);
timing = millis();
esp_task_wdt_reset();
somfy.loop();
if(millis() - timing > 100) ESP_LOGD(TAG, "Timing Somfy: %ldms", millis() - timing);
timing = millis();
esp_task_wdt_reset();
if(net.connected() || net.softAPOpened) {
if(!rebootDelay.reboot && net.connected() && !net.softAPOpened) {
git.loop();
esp_task_wdt_reset();
}
webServer.loop();
esp_task_wdt_reset();
if(millis() - timing > 100) ESP_LOGD(TAG, "Timing WebServer: %ldms", millis() - timing);
esp_task_wdt_reset();
timing = millis();
sockEmit.loop();
if(millis() - timing > 100) ESP_LOGD(TAG, "Timing Socket: %ldms", millis() - timing);
esp_task_wdt_reset();
timing = millis();
}
if(rebootDelay.reboot && millis() > rebootDelay.rebootTime) {
net.end();
ESP.restart();
}
esp_task_wdt_reset();
}

View file

@ -374,6 +374,7 @@ static void serializeTransceiverConfig(JsonFormatter &json) {
json.addElem("txPower", cfg.txPower); json.addElem("txPower", cfg.txPower);
json.addElem("proto", static_cast<uint8_t>(cfg.proto)); json.addElem("proto", static_cast<uint8_t>(cfg.proto));
json.addElem("enabled", cfg.enabled); json.addElem("enabled", cfg.enabled);
json.addElem("noiseDetection", cfg.noiseDetection);
json.addElem("radioInit", cfg.radioInit); json.addElem("radioInit", cfg.radioInit);
} }
static void serializeAppVersion(JsonFormatter &json, appver_t &ver) { static void serializeAppVersion(JsonFormatter &json, appver_t &ver) {