mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 11:16:34 +00:00
Merge branch 'development' into pre-release
This commit is contained in:
commit
b00463900b
@ -136,6 +136,8 @@
|
||||
| USE_GPS | - | - | - | - | - | - | - |
|
||||
| USE_HM10 | - | - | - | - | x | - | - |
|
||||
| USE_HRXL | - | - | - | - | x | - | - |
|
||||
| USE_TASMOTA_SLAVE | - | - | - | - | - | - | - |
|
||||
| USE_OPENTHERM | - | - | - | - | - | - | - |
|
||||
| | | | | | | | |
|
||||
| USE_NRF24 | - | - | - | - | - | - | - |
|
||||
| USE_MIBLE | - | - | - | - | - | - | - |
|
||||
@ -149,11 +151,11 @@
|
||||
| USE_TM1638 | - | - | - | - | x | - | - |
|
||||
| USE_HX711 | - | - | - | - | x | - | - |
|
||||
| USE_TX2x_WIND_SENSOR | - | - | - | - | - | - | - |
|
||||
| USE_WINDMETER | - | - | - | - | - | - | - |
|
||||
| USE_RC_SWITCH | - | - | - | - | x | - | - |
|
||||
| USE_RF_SENSOR | - | - | - | - | x | - | - | AlectoV2 only
|
||||
| USE_HRE | - | - | - | - | x | - | - |
|
||||
| USE_A4988_STEPPER | - | - | - | - | - | - | - |
|
||||
| USE_TASMOTA_SLAVE | - | - | - | - | - | - | - | Experimental
|
||||
| | | | | | | | |
|
||||
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
|
||||
| USE_DISPLAY | - | - | - | - | - | - | x |
|
||||
@ -167,3 +169,5 @@
|
||||
| USE_DISPLAY_ILI9488 | - | - | - | - | - | - | - |
|
||||
| USE_DISPLAY_SSD1351 | - | - | - | - | - | - | - |
|
||||
| USE_DISPLAY_RA8876 | - | - | - | - | - | - | - |
|
||||
| | | | | | | | |
|
||||
| USE_WEBCAM | - | - | - | - | - | - | - | ESP32 only
|
||||
|
@ -57,11 +57,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
||||
- Breaking Change Device Groups multicast address and port (#8270)
|
||||
- Change PWM implementation to Arduino #7231 removing support for Core versions before 2.6.3
|
||||
- Change default PWM Frequency to 223 Hz instead of 880 Hz for less interrupt pressure
|
||||
- Change flash access removing support for any Core before 2.6.3
|
||||
- Change HM-10 sensor type detection and add features (#7962)
|
||||
- Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034)
|
||||
- Change remove floating point libs from IRAM
|
||||
- Change remove MQTT Info messages on restart for DeepSleep Wake (#8044)
|
||||
- Change IRremoteESP8266 library updated to v2.7.6
|
||||
- Change HAss discovery by Federico Leoni (#8370)
|
||||
- Fix possible Relay toggle on (OTA) restart
|
||||
- Fix PWM flickering during wifi connection (#8046)
|
||||
- Fix Zigbee sending wrong Sat value with Hue emulation
|
||||
@ -96,4 +98,6 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
||||
- Add console command history (#7483, #8015)
|
||||
- Add quick wifi reconnect using saved AP parameters when ``SetOption56 0`` (#3189)
|
||||
- Add more accuracy to GPS NTP server (#8088)
|
||||
- Add support for analog anemometer by Matteo Albinola (#8283)
|
||||
- Add support for OpenTherm by Yuriy Sannikov (#8373)
|
||||
- Add experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt
|
||||
|
21
lib/OpenTherm-0.9.0/LICENSE
Normal file
21
lib/OpenTherm-0.9.0/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ihor Melnyk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
65
lib/OpenTherm-0.9.0/README.md
Normal file
65
lib/OpenTherm-0.9.0/README.md
Normal file
@ -0,0 +1,65 @@
|
||||
# OpenTherm Arduino/ESP8266 Library
|
||||
|
||||
This library provides implementation of OpenTherm protocol.
|
||||
|
||||
OpenTherm Library is based on OpenTherm protocol specification v2.2 and works with all OpenTherm compatible boilers. Library can be easily installed into Arduino IDE and compiled for Arduino, ESP8266 and other similar controllers.
|
||||
|
||||
OpenTherm protocol requires simple low voltage twowire connection to boiler, but voltage levels (7..15V) still much higher than Arduino/ESP8266 levels, which requires [OpenTherm Adapter](http://ihormelnyk.com/opentherm_adapter).
|
||||
|
||||
This version of library uses interrupts to achieve better stability and synchronization with boiler.
|
||||
|
||||
## Using OpenTherm Library you will be able:
|
||||
- control your boiler remotely (get status, switch on/off heating/water heating, set water temperature and much more)
|
||||
- make custom thermostat
|
||||
|
||||
## Configuration and Usage:
|
||||
```c
|
||||
#include <OpenTherm.h>
|
||||
```
|
||||
You have to choose 2 controller GPIO pins which will be used for communication and connected to [OpenTherm Adapter](http://ihormelnyk.com/opentherm_adapter). Controller(Arduino/ESP8266) input pin should support interrupts.
|
||||
Controller output pin should be connected to OpenTherm Adapter input pin and vise versa.
|
||||
```c
|
||||
const int inPin = 2;
|
||||
const int outPin = 3;
|
||||
```
|
||||
Define OpenTherm class instance using constructor which accepts as arguments pin numbers:
|
||||
```c
|
||||
OpenTherm ot(inPin, outPin);
|
||||
```
|
||||
Define interrupts handler function for specified above instance:
|
||||
```c
|
||||
void handleInterrupt() {
|
||||
ot.handleInterrupt();
|
||||
}
|
||||
```
|
||||
Use begin function to initialize OpenTherm instance, specify interrupts handler function as argument
|
||||
```c
|
||||
void setup()
|
||||
{
|
||||
ot.begin(handleInterrupt);
|
||||
}
|
||||
```
|
||||
According to OpenTherm Protocol specification master (controller) must communicate at least every 1 sec. So lets make some requests in loop function:
|
||||
```c
|
||||
void loop()
|
||||
{
|
||||
//Set/Get Boiler Status
|
||||
ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
|
||||
//Set Boiler Temperature to 64 degrees C
|
||||
ot.setBoilerTemperature(64);
|
||||
//Get Boiler Temperature
|
||||
float temperature = ot.getBoilerTemperature();
|
||||
delay(1000);
|
||||
}
|
||||
```
|
||||
|
||||
In details [OpenTherm Library](http://ihormelnyk.com/opentherm_library) described [here](http://ihormelnyk.com/opentherm_library).
|
||||
|
||||
## OpenTherm Adapter Schematic
|
||||

|
||||
|
||||
## Arduino UNO Connection
|
||||

|
||||
|
||||
## License
|
||||
Copyright (c) 2018 [Ihor Melnyk](http://ihormelnyk.com). Licensed under the [MIT license](/LICENSE?raw=true).
|
40
lib/OpenTherm-0.9.0/keywords.txt
Normal file
40
lib/OpenTherm-0.9.0/keywords.txt
Normal file
@ -0,0 +1,40 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For OpenTherm
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
OpenTherm KEYWORD1
|
||||
OpenThermStatus KEYWORD1
|
||||
OpenThermResponseStatus KEYWORD1
|
||||
OpenThermRequestType KEYWORD1
|
||||
OpenThermMessageID KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
isReady KEYWORD2
|
||||
sendRequest KEYWORD2
|
||||
sendRequestAync KEYWORD2
|
||||
buildRequest KEYWORD2
|
||||
getLastResponseStatus KEYWORD2
|
||||
handleInterrupt KEYWORD2
|
||||
process KEYWORD2
|
||||
end KEYWORD2
|
||||
doSomething KEYWORD2
|
||||
|
||||
setBoilerStatus KEYWORD2
|
||||
setBoilerTemperature KEYWORD2
|
||||
getBoilerTemperature KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
10
lib/OpenTherm-0.9.0/library.properties
Normal file
10
lib/OpenTherm-0.9.0/library.properties
Normal file
@ -0,0 +1,10 @@
|
||||
name=OpenTherm Library
|
||||
version=0.9.0
|
||||
author=Ihor Melnyk <ihor.melnyk@gmail.com>
|
||||
maintainer=Ihor Melnyk <ihor.melnyk@gmail.com>
|
||||
sentence=OpenTherm Library for HVAC system control communication using Arduino and ESP8266 hardware.
|
||||
paragraph=OpenTherm Library is based on OpenTherm protocol specification v2.2 and works with all OpenTherm compatible boilers.
|
||||
category=Communication
|
||||
url=https://github.com/ihormelnyk/opentherm_library
|
||||
architectures=*
|
||||
includes=OpenTherm.h
|
410
lib/OpenTherm-0.9.0/src/OpenTherm.cpp
Normal file
410
lib/OpenTherm-0.9.0/src/OpenTherm.cpp
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
OpenTherm.cpp - OpenTherm Communication Library For Arduino, ESP8266
|
||||
Copyright 2018, Ihor Melnyk
|
||||
*/
|
||||
|
||||
#include "OpenTherm.h"
|
||||
|
||||
OpenTherm::OpenTherm(int inPin, int outPin, bool isSlave):
|
||||
status(OpenThermStatus::NOT_INITIALIZED),
|
||||
inPin(inPin),
|
||||
outPin(outPin),
|
||||
isSlave(isSlave),
|
||||
response(0),
|
||||
responseStatus(OpenThermResponseStatus::NONE),
|
||||
responseTimestamp(0),
|
||||
handleInterruptCallback(NULL),
|
||||
processResponseCallback(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void OpenTherm::begin(void(*handleInterruptCallback)(void), void(*processResponseCallback)(unsigned long, int))
|
||||
{
|
||||
pinMode(inPin, INPUT);
|
||||
pinMode(outPin, OUTPUT);
|
||||
if (handleInterruptCallback != NULL) {
|
||||
this->handleInterruptCallback = handleInterruptCallback;
|
||||
attachInterrupt(digitalPinToInterrupt(inPin), handleInterruptCallback, CHANGE);
|
||||
}
|
||||
activateBoiler();
|
||||
status = OpenThermStatus::READY;
|
||||
this->processResponseCallback = processResponseCallback;
|
||||
}
|
||||
|
||||
void OpenTherm::begin(void(*handleInterruptCallback)(void))
|
||||
{
|
||||
begin(handleInterruptCallback, NULL);
|
||||
}
|
||||
|
||||
bool ICACHE_RAM_ATTR OpenTherm::isReady()
|
||||
{
|
||||
return status == OpenThermStatus::READY;
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR OpenTherm::readState() {
|
||||
return digitalRead(inPin);
|
||||
}
|
||||
|
||||
void OpenTherm::setActiveState() {
|
||||
digitalWrite(outPin, LOW);
|
||||
}
|
||||
|
||||
void OpenTherm::setIdleState() {
|
||||
digitalWrite(outPin, HIGH);
|
||||
}
|
||||
|
||||
void OpenTherm::activateBoiler() {
|
||||
setIdleState();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void OpenTherm::sendBit(bool high) {
|
||||
if (high) setActiveState(); else setIdleState();
|
||||
delayMicroseconds(500);
|
||||
if (high) setIdleState(); else setActiveState();
|
||||
delayMicroseconds(500);
|
||||
}
|
||||
|
||||
bool OpenTherm::sendRequestAync(unsigned long request)
|
||||
{
|
||||
//Serial.println("Request: " + String(request, HEX));
|
||||
noInterrupts();
|
||||
const bool ready = isReady();
|
||||
interrupts();
|
||||
|
||||
if (!ready)
|
||||
return false;
|
||||
|
||||
status = OpenThermStatus::REQUEST_SENDING;
|
||||
response = 0;
|
||||
responseStatus = OpenThermResponseStatus::NONE;
|
||||
|
||||
sendBit(HIGH); //start bit
|
||||
for (int i = 31; i >= 0; i--) {
|
||||
sendBit(bitRead(request, i));
|
||||
}
|
||||
sendBit(HIGH); //stop bit
|
||||
setIdleState();
|
||||
|
||||
status = OpenThermStatus::RESPONSE_WAITING;
|
||||
responseTimestamp = micros();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::sendRequest(unsigned long request)
|
||||
{
|
||||
if (!sendRequestAync(request)) return 0;
|
||||
while (!isReady()) {
|
||||
process();
|
||||
yield();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
bool OpenTherm::sendResponse(unsigned long request)
|
||||
{
|
||||
status = OpenThermStatus::REQUEST_SENDING;
|
||||
response = 0;
|
||||
responseStatus = OpenThermResponseStatus::NONE;
|
||||
|
||||
sendBit(HIGH); //start bit
|
||||
for (int i = 31; i >= 0; i--) {
|
||||
sendBit(bitRead(request, i));
|
||||
}
|
||||
sendBit(HIGH); //stop bit
|
||||
setIdleState();
|
||||
status = OpenThermStatus::READY;
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenThermResponseStatus OpenTherm::getLastResponseStatus()
|
||||
{
|
||||
return responseStatus;
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR OpenTherm::handleInterrupt()
|
||||
{
|
||||
if (isReady())
|
||||
{
|
||||
if (isSlave && readState() == HIGH) {
|
||||
status = OpenThermStatus::RESPONSE_WAITING;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long newTs = micros();
|
||||
if (status == OpenThermStatus::RESPONSE_WAITING) {
|
||||
if (readState() == HIGH) {
|
||||
status = OpenThermStatus::RESPONSE_START_BIT;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
else {
|
||||
status = OpenThermStatus::RESPONSE_INVALID;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
}
|
||||
else if (status == OpenThermStatus::RESPONSE_START_BIT) {
|
||||
if ((newTs - responseTimestamp < 750) && readState() == LOW) {
|
||||
status = OpenThermStatus::RESPONSE_RECEIVING;
|
||||
responseTimestamp = newTs;
|
||||
responseBitIndex = 0;
|
||||
}
|
||||
else {
|
||||
status = OpenThermStatus::RESPONSE_INVALID;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
}
|
||||
else if (status == OpenThermStatus::RESPONSE_RECEIVING) {
|
||||
if ((newTs - responseTimestamp) > 750) {
|
||||
if (responseBitIndex < 32) {
|
||||
response = (response << 1) | !readState();
|
||||
responseTimestamp = newTs;
|
||||
responseBitIndex++;
|
||||
}
|
||||
else { //stop bit
|
||||
status = OpenThermStatus::RESPONSE_READY;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenTherm::process()
|
||||
{
|
||||
noInterrupts();
|
||||
OpenThermStatus st = status;
|
||||
unsigned long ts = responseTimestamp;
|
||||
interrupts();
|
||||
|
||||
if (st == OpenThermStatus::READY) return;
|
||||
unsigned long newTs = micros();
|
||||
if (st != OpenThermStatus::NOT_INITIALIZED && (newTs - ts) > 1000000) {
|
||||
status = OpenThermStatus::READY;
|
||||
responseStatus = OpenThermResponseStatus::TIMEOUT;
|
||||
if (processResponseCallback != NULL) {
|
||||
processResponseCallback(response, responseStatus);
|
||||
}
|
||||
}
|
||||
else if (st == OpenThermStatus::RESPONSE_INVALID) {
|
||||
status = OpenThermStatus::DELAY;
|
||||
responseStatus = OpenThermResponseStatus::INVALID;
|
||||
if (processResponseCallback != NULL) {
|
||||
processResponseCallback(response, responseStatus);
|
||||
}
|
||||
}
|
||||
else if (st == OpenThermStatus::RESPONSE_READY) {
|
||||
status = OpenThermStatus::DELAY;
|
||||
responseStatus = (isSlave ? isValidRequest(response) : isValidResponse(response)) ? OpenThermResponseStatus::SUCCESS : OpenThermResponseStatus::INVALID;
|
||||
if (processResponseCallback != NULL) {
|
||||
processResponseCallback(response, responseStatus);
|
||||
}
|
||||
}
|
||||
else if (st == OpenThermStatus::DELAY) {
|
||||
if ((newTs - ts) > 100000) {
|
||||
status = OpenThermStatus::READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenTherm::parity(unsigned long frame) //odd parity
|
||||
{
|
||||
byte p = 0;
|
||||
while (frame > 0)
|
||||
{
|
||||
if (frame & 1) p++;
|
||||
frame = frame >> 1;
|
||||
}
|
||||
return (p & 1);
|
||||
}
|
||||
|
||||
OpenThermMessageType OpenTherm::getMessageType(unsigned long message)
|
||||
{
|
||||
OpenThermMessageType msg_type = static_cast<OpenThermMessageType>((message >> 28) & 7);
|
||||
return msg_type;
|
||||
}
|
||||
|
||||
OpenThermMessageID OpenTherm::getDataID(unsigned long frame)
|
||||
{
|
||||
return (OpenThermMessageID)((frame >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data)
|
||||
{
|
||||
unsigned long request = data;
|
||||
if (type == OpenThermMessageType::WRITE_DATA) {
|
||||
request |= 1ul << 28;
|
||||
}
|
||||
request |= ((unsigned long)id) << 16;
|
||||
if (OpenTherm::parity(request)) request |= (1ul << 31);
|
||||
return request;
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data)
|
||||
{
|
||||
unsigned long response = data;
|
||||
response |= type << 28;
|
||||
response |= ((unsigned long)id) << 16;
|
||||
if (OpenTherm::parity(response)) response |= (1ul << 31);
|
||||
return response;
|
||||
}
|
||||
|
||||
bool OpenTherm::isValidResponse(unsigned long response)
|
||||
{
|
||||
if (OpenTherm::parity(response)) return false;
|
||||
byte msgType = (response << 1) >> 29;
|
||||
return msgType == READ_ACK || msgType == WRITE_ACK;
|
||||
}
|
||||
|
||||
bool OpenTherm::isValidRequest(unsigned long request)
|
||||
{
|
||||
if (OpenTherm::parity(request)) return false;
|
||||
byte msgType = (request << 1) >> 29;
|
||||
return msgType == READ_DATA || msgType == WRITE_DATA;
|
||||
}
|
||||
|
||||
void OpenTherm::end() {
|
||||
if (this->handleInterruptCallback != NULL) {
|
||||
detachInterrupt(digitalPinToInterrupt(inPin));
|
||||
}
|
||||
}
|
||||
|
||||
const char *OpenTherm::statusToString(OpenThermResponseStatus status)
|
||||
{
|
||||
switch (status) {
|
||||
case NONE: return "NONE";
|
||||
case SUCCESS: return "SUCCESS";
|
||||
case INVALID: return "INVALID";
|
||||
case TIMEOUT: return "TIMEOUT";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *OpenTherm::messageTypeToString(OpenThermMessageType message_type)
|
||||
{
|
||||
switch (message_type) {
|
||||
case READ_DATA: return "READ_DATA";
|
||||
case WRITE_DATA: return "WRITE_DATA";
|
||||
case INVALID_DATA: return "INVALID_DATA";
|
||||
case RESERVED: return "RESERVED";
|
||||
case READ_ACK: return "READ_ACK";
|
||||
case WRITE_ACK: return "WRITE_ACK";
|
||||
case DATA_INVALID: return "DATA_INVALID";
|
||||
case UNKNOWN_DATA_ID: return "UNKNOWN_DATA_ID";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
//building requests
|
||||
|
||||
unsigned long OpenTherm::buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2) {
|
||||
unsigned int data = enableCentralHeating | (enableHotWater << 1) | (enableCooling << 2) | (enableOutsideTemperatureCompensation << 3) | (enableCentralHeating2 << 4);
|
||||
data <<= 8;
|
||||
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Status, data);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildSetBoilerTemperatureRequest(float temperature) {
|
||||
unsigned int data = temperatureToData(temperature);
|
||||
return buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildSetHotWaterTemperatureRequest(float temperature) {
|
||||
unsigned int data = temperatureToData(temperature);
|
||||
return buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildGetBoilerTemperatureRequest() {
|
||||
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tboiler, 0);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildSlaveConfigurationRequest() {
|
||||
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0);
|
||||
}
|
||||
|
||||
//parsing responses
|
||||
bool OpenTherm::isFault(unsigned long response) {
|
||||
return response & 0x1;
|
||||
}
|
||||
|
||||
bool OpenTherm::isCentralHeatingActive(unsigned long response) {
|
||||
return response & 0x2;
|
||||
}
|
||||
|
||||
bool OpenTherm::isHotWaterActive(unsigned long response) {
|
||||
return response & 0x4;
|
||||
}
|
||||
|
||||
bool OpenTherm::isFlameOn(unsigned long response) {
|
||||
return response & 0x8;
|
||||
}
|
||||
|
||||
bool OpenTherm::isCoolingActive(unsigned long response) {
|
||||
return response & 0x10;
|
||||
}
|
||||
|
||||
bool OpenTherm::isDiagnostic(unsigned long response) {
|
||||
return response & 0x40;
|
||||
}
|
||||
|
||||
uint16_t OpenTherm::getUInt(const unsigned long response) {
|
||||
const uint16_t u88 = response & 0xffff;
|
||||
return u88;
|
||||
}
|
||||
|
||||
float OpenTherm::getFloat(const unsigned long response) {
|
||||
const uint16_t u88 = getUInt(response);
|
||||
const float f = (u88 & 0x8000) ? -(0x10000L - u88) / 256.0f : u88 / 256.0f;
|
||||
return f;
|
||||
}
|
||||
|
||||
unsigned int OpenTherm::temperatureToData(float temperature) {
|
||||
if (temperature < 0) temperature = 0;
|
||||
if (temperature > 100) temperature = 100;
|
||||
unsigned int data = (unsigned int)(temperature * 256);
|
||||
return data;
|
||||
}
|
||||
|
||||
//basic requests
|
||||
|
||||
unsigned long OpenTherm::setBoilerStatus(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2) {
|
||||
return sendRequest(buildSetBoilerStatusRequest(enableCentralHeating, enableHotWater, enableCooling, enableOutsideTemperatureCompensation, enableCentralHeating2));
|
||||
}
|
||||
|
||||
bool OpenTherm::setBoilerTemperature(float temperature) {
|
||||
unsigned long response = sendRequest(buildSetBoilerTemperatureRequest(temperature));
|
||||
return isValidResponse(response);
|
||||
}
|
||||
|
||||
bool OpenTherm::setHotWaterTemperature(float temperature) {
|
||||
unsigned long response = sendRequest(buildSetHotWaterTemperatureRequest(temperature));
|
||||
return isValidResponse(response);
|
||||
}
|
||||
|
||||
float OpenTherm::getBoilerTemperature() {
|
||||
unsigned long response = sendRequest(buildGetBoilerTemperatureRequest());
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
float OpenTherm::getReturnTemperature() {
|
||||
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tret, 0));
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
float OpenTherm::getModulation() {
|
||||
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
float OpenTherm::getPressure() {
|
||||
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
unsigned char OpenTherm::getFault() {
|
||||
return ((sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0)) >> 8) & 0xff);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::getSlaveConfiguration() {
|
||||
return sendRequest(buildSlaveConfigurationRequest());
|
||||
}
|
193
lib/OpenTherm-0.9.0/src/OpenTherm.h
Normal file
193
lib/OpenTherm-0.9.0/src/OpenTherm.h
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
OpenTherm.h - OpenTherm Library for the ESP8266/Arduino platform
|
||||
https://github.com/ihormelnyk/OpenTherm
|
||||
http://ihormelnyk.com/pages/OpenTherm
|
||||
Licensed under MIT license
|
||||
Copyright 2018, Ihor Melnyk
|
||||
|
||||
Frame Structure:
|
||||
P MGS-TYPE SPARE DATA-ID DATA-VALUE
|
||||
0 000 0000 00000000 00000000 00000000
|
||||
*/
|
||||
|
||||
#ifndef OpenTherm_h
|
||||
#define OpenTherm_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
enum OpenThermResponseStatus {
|
||||
NONE,
|
||||
SUCCESS,
|
||||
INVALID,
|
||||
TIMEOUT
|
||||
};
|
||||
|
||||
|
||||
enum OpenThermMessageType {
|
||||
/* Master to Slave */
|
||||
READ_DATA = B000,
|
||||
READ = READ_DATA, // for backwared compatibility
|
||||
WRITE_DATA = B001,
|
||||
WRITE = WRITE_DATA, // for backwared compatibility
|
||||
INVALID_DATA = B010,
|
||||
RESERVED = B011,
|
||||
/* Slave to Master */
|
||||
READ_ACK = B100,
|
||||
WRITE_ACK = B101,
|
||||
DATA_INVALID = B110,
|
||||
UNKNOWN_DATA_ID = B111
|
||||
};
|
||||
|
||||
typedef OpenThermMessageType OpenThermRequestType; // for backwared compatibility
|
||||
|
||||
enum OpenThermMessageID {
|
||||
Status, // flag8 / flag8 Master and Slave Status flags.
|
||||
TSet, // f8.8 Control setpoint ie CH water temperature setpoint (°C)
|
||||
MConfigMMemberIDcode, // flag8 / u8 Master Configuration Flags / Master MemberID Code
|
||||
SConfigSMemberIDcode, // flag8 / u8 Slave Configuration Flags / Slave MemberID Code
|
||||
Command, // u8 / u8 Remote Command
|
||||
ASFflags, // / OEM-fault-code flag8 / u8 Application-specific fault flags and OEM fault code
|
||||
RBPflags, // flag8 / flag8 Remote boiler parameter transfer-enable & read/write flags
|
||||
CoolingControl, // f8.8 Cooling control signal (%)
|
||||
TsetCH2, // f8.8 Control setpoint for 2e CH circuit (°C)
|
||||
TrOverride, // f8.8 Remote override room setpoint
|
||||
TSP, // u8 / u8 Number of Transparent-Slave-Parameters supported by slave
|
||||
TSPindexTSPvalue, // u8 / u8 Index number / Value of referred-to transparent slave parameter.
|
||||
FHBsize, // u8 / u8 Size of Fault-History-Buffer supported by slave
|
||||
FHBindexFHBvalue, // u8 / u8 Index number / Value of referred-to fault-history buffer entry.
|
||||
MaxRelModLevelSetting, // f8.8 Maximum relative modulation level setting (%)
|
||||
MaxCapacityMinModLevel, // u8 / u8 Maximum boiler capacity (kW) / Minimum boiler modulation level(%)
|
||||
TrSet, // f8.8 Room Setpoint (°C)
|
||||
RelModLevel, // f8.8 Relative Modulation Level (%)
|
||||
CHPressure, // f8.8 Water pressure in CH circuit (bar)
|
||||
DHWFlowRate, // f8.8 Water flow rate in DHW circuit. (litres/minute)
|
||||
DayTime, // special / u8 Day of Week and Time of Day
|
||||
Date, // u8 / u8 Calendar date
|
||||
Year, // u16 Calendar year
|
||||
TrSetCH2, // f8.8 Room Setpoint for 2nd CH circuit (°C)
|
||||
Tr, // f8.8 Room temperature (°C)
|
||||
Tboiler, // f8.8 Boiler flow water temperature (°C)
|
||||
Tdhw, // f8.8 DHW temperature (°C)
|
||||
Toutside, // f8.8 Outside temperature (°C)
|
||||
Tret, // f8.8 Return water temperature (°C)
|
||||
Tstorage, // f8.8 Solar storage temperature (°C)
|
||||
Tcollector, // f8.8 Solar collector temperature (°C)
|
||||
TflowCH2, // f8.8 Flow water temperature CH2 circuit (°C)
|
||||
Tdhw2, // f8.8 Domestic hot water temperature 2 (°C)
|
||||
Texhaust, // s16 Boiler exhaust temperature (°C)
|
||||
TdhwSetUBTdhwSetLB = 48, // s8 / s8 DHW setpoint upper & lower bounds for adjustment (°C)
|
||||
MaxTSetUBMaxTSetLB, // s8 / s8 Max CH water setpoint upper & lower bounds for adjustment (°C)
|
||||
HcratioUBHcratioLB, // s8 / s8 OTC heat curve ratio upper & lower bounds for adjustment
|
||||
TdhwSet = 56, // f8.8 DHW setpoint (°C) (Remote parameter 1)
|
||||
MaxTSet, // f8.8 Max CH water setpoint (°C) (Remote parameters 2)
|
||||
Hcratio, // f8.8 OTC heat curve ratio (°C) (Remote parameter 3)
|
||||
RemoteOverrideFunction = 100, // flag8 / - Function of manual and program changes in master and remote room setpoint.
|
||||
OEMDiagnosticCode = 115, // u16 OEM-specific diagnostic/service code
|
||||
BurnerStarts, // u16 Number of starts burner
|
||||
CHPumpStarts, // u16 Number of starts CH pump
|
||||
DHWPumpValveStarts, // u16 Number of starts DHW pump/valve
|
||||
DHWBurnerStarts, // u16 Number of starts burner during DHW mode
|
||||
BurnerOperationHours, // u16 Number of hours that burner is in operation (i.e. flame on)
|
||||
CHPumpOperationHours, // u16 Number of hours that CH pump has been running
|
||||
DHWPumpValveOperationHours, // u16 Number of hours that DHW pump has been running or DHW valve has been opened
|
||||
DHWBurnerOperationHours, // u16 Number of hours that burner is in operation during DHW mode
|
||||
OpenThermVersionMaster, // f8.8 The implemented version of the OpenTherm Protocol Specification in the master.
|
||||
OpenThermVersionSlave, // f8.8 The implemented version of the OpenTherm Protocol Specification in the slave.
|
||||
MasterVersion, // u8 / u8 Master product version number and type
|
||||
SlaveVersion, // u8 / u8 Slave product version number and type
|
||||
};
|
||||
|
||||
enum OpenThermStatus {
|
||||
NOT_INITIALIZED,
|
||||
READY,
|
||||
DELAY,
|
||||
REQUEST_SENDING,
|
||||
RESPONSE_WAITING,
|
||||
RESPONSE_START_BIT,
|
||||
RESPONSE_RECEIVING,
|
||||
RESPONSE_READY,
|
||||
RESPONSE_INVALID
|
||||
};
|
||||
|
||||
class OpenTherm
|
||||
{
|
||||
public:
|
||||
OpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false);
|
||||
volatile OpenThermStatus status;
|
||||
void begin(void(*handleInterruptCallback)(void));
|
||||
void begin(void(*handleInterruptCallback)(void), void(*processResponseCallback)(unsigned long, int));
|
||||
bool isReady();
|
||||
unsigned long sendRequest(unsigned long request);
|
||||
bool sendResponse(unsigned long request);
|
||||
bool sendRequestAync(unsigned long request);
|
||||
static unsigned long buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data);
|
||||
static unsigned long buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data);
|
||||
OpenThermResponseStatus getLastResponseStatus();
|
||||
const char *statusToString(OpenThermResponseStatus status);
|
||||
void handleInterrupt();
|
||||
void process();
|
||||
void end();
|
||||
|
||||
static bool parity(unsigned long frame);
|
||||
OpenThermMessageType getMessageType(unsigned long message);
|
||||
OpenThermMessageID getDataID(unsigned long frame);
|
||||
const char *messageTypeToString(OpenThermMessageType message_type);
|
||||
bool isValidRequest(unsigned long request);
|
||||
bool isValidResponse(unsigned long response);
|
||||
|
||||
//requests
|
||||
unsigned long buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false);
|
||||
unsigned long buildSetBoilerTemperatureRequest(float temperature);
|
||||
unsigned long buildGetBoilerTemperatureRequest();
|
||||
unsigned long buildSetHotWaterTemperatureRequest(float temperature);
|
||||
unsigned long buildSlaveConfigurationRequest();
|
||||
|
||||
|
||||
//responses
|
||||
static bool isFault(unsigned long response);
|
||||
static bool isCentralHeatingActive(unsigned long response);
|
||||
static bool isHotWaterActive(unsigned long response);
|
||||
static bool isFlameOn(unsigned long response);
|
||||
static bool isCoolingActive(unsigned long response);
|
||||
static bool isDiagnostic(unsigned long response);
|
||||
static uint16_t getUInt(const unsigned long response);
|
||||
static float getFloat(const unsigned long response);
|
||||
static unsigned int temperatureToData(float temperature);
|
||||
|
||||
//basic requests
|
||||
unsigned long setBoilerStatus(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false);
|
||||
bool setBoilerTemperature(float temperature);
|
||||
bool setHotWaterTemperature(float temperature);
|
||||
float getBoilerTemperature();
|
||||
float getReturnTemperature();
|
||||
float getModulation();
|
||||
float getPressure();
|
||||
unsigned char getFault();
|
||||
unsigned long getSlaveConfiguration();
|
||||
|
||||
private:
|
||||
const int inPin;
|
||||
const int outPin;
|
||||
const bool isSlave;
|
||||
|
||||
volatile unsigned long response;
|
||||
volatile OpenThermResponseStatus responseStatus;
|
||||
volatile unsigned long responseTimestamp;
|
||||
volatile byte responseBitIndex;
|
||||
|
||||
int readState();
|
||||
void setActiveState();
|
||||
void setIdleState();
|
||||
void activateBoiler();
|
||||
|
||||
void sendBit(bool high);
|
||||
void(*handleInterruptCallback)();
|
||||
void(*processResponseCallback)(unsigned long, int);
|
||||
};
|
||||
|
||||
#ifndef ICACHE_RAM_ATTR
|
||||
#define ICACHE_RAM_ATTR
|
||||
#endif
|
||||
|
||||
#endif // OpenTherm_h
|
9
pio/override_copy.py
Normal file
9
pio/override_copy.py
Normal file
@ -0,0 +1,9 @@
|
||||
Import('env')
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# copy tasmota/user_config_override_sample.h to tasmota/user_config_override.h
|
||||
if os.path.isfile("tasmota/user_config_override.h"):
|
||||
print ("*** use provided user_config_override.h as planned ***")
|
||||
else:
|
||||
shutil.copy("tasmota/user_config_override_sample.h", "tasmota/user_config_override.h")
|
@ -8,17 +8,19 @@
|
||||
; http://docs.platformio.org/en/stable/projectconf.html
|
||||
|
||||
[platformio]
|
||||
description = Provide ESP8266 based devices with Web, MQTT and OTA firmware
|
||||
src_dir = tasmota
|
||||
build_dir = .pioenvs
|
||||
workspace_dir = .pioenvs
|
||||
build_cache_dir = .cache
|
||||
extra_configs = platformio_tasmota_env.ini
|
||||
platformio_override.ini
|
||||
|
||||
; *** Build/upload environment
|
||||
default_envs =
|
||||
default_envs =
|
||||
; *** Uncomment by deleting ";" in the line(s) below to select version(s)
|
||||
; tasmota
|
||||
; tasmota-ircustom
|
||||
; tasmota-ircustom
|
||||
; tasmota-minimal
|
||||
; tasmota-lite
|
||||
; tasmota-knx
|
||||
@ -64,11 +66,8 @@ platform_packages = ${core_active.platform_packages}
|
||||
build_flags = ${core_active.build_flags}
|
||||
|
||||
; *********************************************************************
|
||||
; *** Uncomment, by deleting ";" in line below, to use custom settings from file user_config_override.h
|
||||
; -DUSE_CONFIG_OVERRIDE
|
||||
;
|
||||
; *** alternatively can be done in: platformio_override.ini
|
||||
; *** See example: platformio_override_sample.ini
|
||||
; *** Use custom settings from file user_config_override.h
|
||||
-DUSE_CONFIG_OVERRIDE
|
||||
; *********************************************************************
|
||||
|
||||
; *** Fix espressif8266@1.7.0 induced undesired all warnings
|
||||
@ -86,6 +85,7 @@ extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
extra_scripts = pio/strip-floats.py
|
||||
pio/name-firmware.py
|
||||
pio/gzip-firmware.py
|
||||
pio/override_copy.py
|
||||
|
||||
[esp_defaults]
|
||||
build_flags = -D_IR_ENABLE_DEFAULT_=false
|
||||
|
@ -9,7 +9,10 @@
|
||||
### 8.2.0.6 20200501
|
||||
|
||||
- Add experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt
|
||||
- Change PWM updated to latest Arduino Core #7213
|
||||
- Add support for analog anemometer by Matteo Albinola (#8283)
|
||||
- Add support for OpenTherm by Yuriy Sannikov (#8373)
|
||||
- Change flash access removing support for any Core before 2.6.3
|
||||
- Change HAss discovery by Federico Leoni (#8370)
|
||||
|
||||
### 8.2.0.5 20200425
|
||||
|
||||
|
@ -558,6 +558,7 @@
|
||||
#define D_CMND_SHUTTER_RELAY "Relay"
|
||||
#define D_CMND_SHUTTER_SETHALFWAY "SetHalfway"
|
||||
#define D_CMND_SHUTTER_SETCLOSE "SetClose"
|
||||
#define D_CMND_SHUTTER_SETOPEN "SetOpen"
|
||||
#define D_CMND_SHUTTER_INVERT "Invert"
|
||||
#define D_CMND_SHUTTER_CLIBRATION "Calibration"
|
||||
#define D_CMND_SHUTTER_MOTORDELAY "MotorDelay"
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Частици"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Жест"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Грийн"
|
||||
#define D_COLOR_BLUE "син"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "близост"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Ускорение - ос X"
|
||||
#define D_AY_AXIS "Ускорение - ос Y"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_BG_BG_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "částic"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesto"
|
||||
#define D_COLOR_RED "Červená"
|
||||
#define D_COLOR_GREEN "Zelená"
|
||||
#define D_COLOR_BLUE "Modrá"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Blízkost"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. osa-X"
|
||||
#define D_AY_AXIS "Accel. osa-Y"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_CS_CZ_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partikel"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Geste"
|
||||
#define D_COLOR_RED "Rot"
|
||||
#define D_COLOR_GREEN "Grün"
|
||||
#define D_COLOR_BLUE "Blau"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Nähe"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Beschl. X-Achse"
|
||||
#define D_AY_AXIS "Beschl. Y-Achse"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "Kalibrierung fehlerhaft"
|
||||
#define D_AS3935_CAL_OK "Cap gesetzt auf:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_DE_DE_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particals"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Χειρονομία"
|
||||
#define D_COLOR_RED "Κόκκινο"
|
||||
#define D_COLOR_GREEN "Πράσινο"
|
||||
#define D_COLOR_BLUE "Μπλε"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Εγγύτητα"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_EL_GR_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particles"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -687,6 +696,7 @@
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "°K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -785,4 +795,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_EN_GB_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partículas"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesto"
|
||||
#define D_COLOR_RED "Rojo"
|
||||
#define D_COLOR_GREEN "Verde"
|
||||
#define D_COLOR_BLUE "Azul"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximidad"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_ES_ES_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particules"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Geste"
|
||||
#define D_COLOR_RED "Rouge"
|
||||
#define D_COLOR_GREEN "Vert"
|
||||
#define D_COLOR_BLUE "Bleu"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximité"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accél. Axe-X"
|
||||
#define D_AY_AXIS "Accél. Axe-Y"
|
||||
@ -503,7 +511,7 @@
|
||||
#define D_CALIBRATE "Étalonner"
|
||||
#define D_CALIBRATION "Étalonnage"
|
||||
|
||||
//xsns_35_TX20.ino
|
||||
// xsns_35_TX20.ino
|
||||
#define D_TX20_WIND_DIRECTION "Direction du vent"
|
||||
#define D_TX20_WIND_SPEED "Vitesse du vent"
|
||||
#define D_TX20_WIND_SPEED_MIN "Vitesse Min"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_FR_FR_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "חלקיקים"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_HE_HE_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Részecskék"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesztus"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "közelség"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Gyorsulásm. X-tengely"
|
||||
#define D_AY_AXIS "Gyorsulásm. Y-tengely"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_HU_HU_H_
|
||||
|
@ -62,7 +62,7 @@
|
||||
#define D_BRIGHTLIGHT "Luminoso"
|
||||
#define D_BSSID "BSSId"
|
||||
#define D_BUTTON "Pulsante"
|
||||
#define D_BY "da" // Written by me
|
||||
#define D_BY "di" // Written by me
|
||||
#define D_BYTES "Byte"
|
||||
#define D_CELSIUS "Celsius"
|
||||
#define D_CHANNEL "Canale"
|
||||
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particelle"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesto"
|
||||
#define D_COLOR_RED "Rosso"
|
||||
#define D_COLOR_GREEN "Verde"
|
||||
#define D_COLOR_BLUE "Blu"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Vicinanza"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accelerazione asse X"
|
||||
#define D_AY_AXIS "Accelerazione asse Y"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL - RX"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -763,26 +772,30 @@
|
||||
#define D_SCRIPT_UPLOAD_FILES "Upload file"
|
||||
|
||||
//xsns_67_as3935.ino
|
||||
#define D_AS3935_GAIN "gain:"
|
||||
#define D_AS3935_ENERGY "energy:"
|
||||
#define D_AS3935_DISTANCE "distance:"
|
||||
#define D_AS3935_DISTURBER "disturber:"
|
||||
#define D_AS3935_GAIN "guadagno:"
|
||||
#define D_AS3935_ENERGY "energia:"
|
||||
#define D_AS3935_DISTANCE "distanza:"
|
||||
#define D_AS3935_DISTURBER "disturbatore:"
|
||||
#define D_AS3935_VRMS "µVrms:"
|
||||
#define D_AS3935_APRX "aprx.:"
|
||||
#define D_AS3935_AWAY "away"
|
||||
#define D_AS3935_LIGHT "lightning"
|
||||
#define D_AS3935_OUT "lightning out of range"
|
||||
#define D_AS3935_NOT "distance not determined"
|
||||
#define D_AS3935_ABOVE "lightning overhead"
|
||||
#define D_AS3935_NOISE "noise detected"
|
||||
#define D_AS3935_DISTDET "disturber detected"
|
||||
#define D_AS3935_INTNOEV "Interrupt with no Event!"
|
||||
#define D_AS3935_NOMESS "listening..."
|
||||
#define D_AS3935_ON "On"
|
||||
#define D_AS3935_OFF "Off"
|
||||
#define D_AS3935_INDOORS "Indoors"
|
||||
#define D_AS3935_OUTDOORS "Outdoors"
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
#define D_AS3935_APRX "apross.:"
|
||||
#define D_AS3935_AWAY "lontano"
|
||||
#define D_AS3935_LIGHT "illuminazione"
|
||||
#define D_AS3935_OUT "illuminazione fuori intervallo"
|
||||
#define D_AS3935_NOT "distanza non determinata"
|
||||
#define D_AS3935_ABOVE "illuminazione ambientale"
|
||||
#define D_AS3935_NOISE "rilevato rumore"
|
||||
#define D_AS3935_DISTDET "rilevato disturbatore"
|
||||
#define D_AS3935_INTNOEV "Interrupt senza evento!"
|
||||
#define D_AS3935_NOMESS "in ascolto..."
|
||||
#define D_AS3935_ON "ON"
|
||||
#define D_AS3935_OFF "OFF"
|
||||
#define D_AS3935_INDOORS "Interno"
|
||||
#define D_AS3935_OUTDOORS "Esterno"
|
||||
#define D_AS3935_CAL_FAIL "calibrazione fallita"
|
||||
#define D_AS3935_CAL_OK "calibrazione impostata a:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_IT_IT_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "입자"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_KO_KO_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Stofdeeltjes"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Versn. X-as"
|
||||
#define D_AY_AXIS "Versn. Y-as"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_NL_NL_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Cząstki"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_PL_PL_D_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partículas"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_PT_BR_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partículas"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_PT_PT_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particule"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel.Axa-X"
|
||||
#define D_AY_AXIS "Accel.Axa-Y"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_RO_RO_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particals"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_RU_RU_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "častíc"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. os-X"
|
||||
#define D_AY_AXIS "Accel. os-Y"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_SK_SK_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partiklar"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axel"
|
||||
#define D_AY_AXIS "Accel. Y-Axel"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_SV_SE_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particals"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_TR_TR_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Частинки понад"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Приск. Вісь-X"
|
||||
#define D_AY_AXIS "Приск. Вісь-Y"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_UK_UA_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "颗粒物直径大于"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "加速度计X轴分量"
|
||||
#define D_AY_AXIS "加速度计Y轴分量"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_ZH_CN_H_
|
||||
|
@ -482,6 +482,14 @@
|
||||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "顆粒物直徑大於"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
@ -666,6 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL Rx"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -785,4 +794,8 @@
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_ZH_TW_H_
|
||||
|
@ -478,6 +478,10 @@
|
||||
// #define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
|
||||
// #define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
|
||||
// #define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
|
||||
#define USE_APDS9960_GESTURE // Enable APDS9960 Gesture feature (+2k code)
|
||||
#define USE_APDS9960_PROXIMITY // Enable APDS9960 Proximity feature (>50 code)
|
||||
#define USE_APDS9960_COLOR // Enable APDS9960 Color feature (+0.8k code)
|
||||
#define USE_APDS9960_STARTMODE 0 // Default to enable Gesture mode
|
||||
// #define USE_MCP230xx // [I2cDriver22] Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code)
|
||||
// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x26 - set according to your wired setup)
|
||||
// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code)
|
||||
@ -574,6 +578,10 @@
|
||||
//#define USE_HM10 // (ESP8266 only) Add support for HM-10 as a BLE-bridge (+9k3 code)
|
||||
//#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
|
||||
//#define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
|
||||
//#define USE_TASMOTA_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k6 code, 64 mem)
|
||||
#define USE_TASMOTA_SLAVE_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants
|
||||
#define USE_TASMOTA_SLAVE_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini
|
||||
//#define USE_OPENTHERM // Add support for OpenTherm (+15k code)
|
||||
|
||||
// -- Power monitoring sensors --------------------
|
||||
#define USE_ENERGY_MARGIN_DETECTION // Add support for Energy Margin detection (+1k6 code)
|
||||
@ -649,6 +657,8 @@
|
||||
//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k6/0k8 code)
|
||||
//#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+2k7/1k code)
|
||||
|
||||
//#define USE_WINDMETER // Add support for analog anemometer (+2k2 code)
|
||||
|
||||
//#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram)
|
||||
|
||||
//#define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code)
|
||||
@ -658,9 +668,34 @@
|
||||
//#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code)
|
||||
//#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code)
|
||||
|
||||
//#define USE_TASMOTA_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k6 code, 64 mem)
|
||||
#define USE_TASMOTA_SLAVE_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants
|
||||
#define USE_TASMOTA_SLAVE_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini
|
||||
// -- Thermostat control ----------------------------
|
||||
//#define USE_THERMOSTAT // Add support for Thermostat
|
||||
#define THERMOSTAT_CONTROLLER_OUTPUTS 1 // Number of outputs to be controlled independently
|
||||
#define THERMOSTAT_SENSOR_NAME "DS18B20" // Name of the local sensor to be used
|
||||
#define THERMOSTAT_RELAY_NUMBER 1 // Default output relay number for the first controller (+i for following ones)
|
||||
#define THERMOSTAT_SWITCH_NUMBER 1 // Default input switch number for the first controller (+i for following ones)
|
||||
#define THERMOSTAT_TIME_ALLOW_RAMPUP 300 // Default time in seconds after last target update to allow ramp-up controller phase in minutes
|
||||
#define THERMOSTAT_TIME_RAMPUP_MAX 960 // Default time maximum ramp-up controller duration in minutes
|
||||
#define THERMOSTAT_TIME_RAMPUP_CYCLE 1800 // Default time ramp-up cycle in seconds
|
||||
#define THERMOSTAT_TIME_SENS_LOST 30 // Maximum time w/o sensor update to set it as lost in minutes
|
||||
#define THERMOSTAT_TEMP_SENS_NUMBER 1 // Default temperature sensor number
|
||||
#define THERMOSTAT_TIME_MANUAL_TO_AUTO 60 // Default time without input switch active to change from manual to automatic in minutes
|
||||
#define THERMOSTAT_TIME_ON_LIMIT 120 // Default maximum time with output active in minutes
|
||||
#define THERMOSTAT_TIME_RESET 12000 // Default reset time of the PI controller in seconds
|
||||
#define THERMOSTAT_TIME_PI_CYCLE 30 // Default cycle time for the thermostat controller in minutes
|
||||
#define THERMOSTAT_TIME_MAX_ACTION 20 // Default maximum thermostat time per cycle in minutes
|
||||
#define THERMOSTAT_TIME_MIN_ACTION 4 // Default minimum thermostat time per cycle in minutes
|
||||
#define THERMOSTAT_TIME_MIN_TURNOFF_ACTION 3 // Default minimum turnoff time in minutes, below it the thermostat will be held on
|
||||
#define THERMOSTAT_PROP_BAND 4 // Default proportional band of the PI controller in degrees celsius
|
||||
#define THERMOSTAT_TEMP_RESET_ANTI_WINDUP 8 // Default range where reset antiwindup is disabled, in tenths of degrees celsius
|
||||
#define THERMOSTAT_TEMP_HYSTERESIS 1 // Default range hysteresis for temperature PI controller, in tenths of degrees celsius
|
||||
#define THERMOSTAT_TEMP_FROST_PROTECT 40 // Default minimum temperature for frost protection, in tenths of degrees celsius
|
||||
#define THERMOSTAT_TEMP_RAMPUP_DELTA_IN 4 // Default minimum delta temperature to target to get into rampup mode, in tenths of degrees celsius
|
||||
#define THERMOSTAT_TEMP_RAMPUP_DELTA_OUT 2 // Default minimum delta temperature to target to get out of the rampup mode, in tenths of degrees celsius
|
||||
#define THERMOSTAT_TEMP_PI_RAMPUP_ACC_E 200 // Default accumulated error when switching from ramp-up controller to PI in hundreths of degrees celsius
|
||||
#define THERMOSTAT_TIME_OUTPUT_DELAY 180 // Default output delay between state change and real actuation event (f.i. valve open/closed)
|
||||
#define THERMOSTAT_TEMP_INIT 180 // Default init target temperature for the thermostat controller
|
||||
#define THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST 3 // Default maximum time where the input and the outpus shall differ (for diagnostic) in minutes
|
||||
|
||||
// -- End of general directives -------------------
|
||||
|
||||
|
@ -481,6 +481,12 @@ struct {
|
||||
uint8_t shutter_position[MAX_SHUTTERS]; // E80
|
||||
uint8_t shutter_startrelay[MAX_SHUTTERS]; // E84
|
||||
uint8_t pcf8574_config[MAX_PCF8574]; // E88
|
||||
uint8_t ot_hot_water_setpoint; // E8C
|
||||
uint8_t ot_boiler_setpoint; // E8D
|
||||
uint8_t ot_flags; // E8E
|
||||
|
||||
uint8_t free_e8f[1]; // E8F
|
||||
|
||||
uint16_t dimmer_hw_min; // E90
|
||||
uint16_t dimmer_hw_max; // E92
|
||||
uint32_t deepsleep; // E94
|
||||
@ -527,8 +533,13 @@ struct {
|
||||
uint8_t zb_free_byte; // F33
|
||||
uint16_t pms_wake_interval; // F34
|
||||
uint8_t config_version; // F36
|
||||
uint8_t windmeter_pulses_x_rot; // F37
|
||||
uint16_t windmeter_radius; // F38
|
||||
uint16_t windmeter_pulse_debounce; // F3A
|
||||
int16_t windmeter_speed_factor; // F3C
|
||||
uint8_t windmeter_tele_pchange; // F3E
|
||||
|
||||
uint8_t free_f37[129]; // F37 - Decrement if adding new Setting variables just above and below
|
||||
uint8_t free_f3f[121]; // F3F - Decrement if adding new Setting variables just above and below
|
||||
|
||||
// Only 32 bit boundary variables below
|
||||
uint16_t pulse_counter_debounce_low; // FB8
|
||||
|
@ -715,6 +715,12 @@ void SettingsDefaultSet2(void)
|
||||
{
|
||||
memset((char*)&Settings +16, 0x00, sizeof(Settings) -16);
|
||||
|
||||
// this little trick allows GCC to optimize the assignment by grouping values and doing only ORs
|
||||
SysBitfield flag = { 0 };
|
||||
SysBitfield2 flag2 = { 0 };
|
||||
SysBitfield3 flag3 = { 0 };
|
||||
SysBitfield4 flag4 = { 0 };
|
||||
|
||||
#ifdef ESP8266
|
||||
// Settings.config_version = 0; // ESP8266 (Has been 0 for long time)
|
||||
#endif // ESP8266
|
||||
@ -722,13 +728,13 @@ void SettingsDefaultSet2(void)
|
||||
Settings.config_version = 1; // ESP32
|
||||
#endif // ESP32
|
||||
|
||||
Settings.flag.stop_flash_rotate = APP_FLASH_CYCLE;
|
||||
Settings.flag.global_state = APP_ENABLE_LEDLINK;
|
||||
Settings.flag3.sleep_normal = APP_NORMAL_SLEEP;
|
||||
Settings.flag3.no_power_feedback = APP_NO_RELAY_SCAN;
|
||||
Settings.flag3.fast_power_cycle_disable = APP_DISABLE_POWERCYCLE;
|
||||
Settings.flag3.bootcount_update = DEEPSLEEP_BOOTCOUNT;
|
||||
Settings.flag3.compatibility_check = OTA_COMPATIBILITY;
|
||||
flag.stop_flash_rotate |= APP_FLASH_CYCLE;
|
||||
flag.global_state |= APP_ENABLE_LEDLINK;
|
||||
flag3.sleep_normal |= APP_NORMAL_SLEEP;
|
||||
flag3.no_power_feedback |= APP_NO_RELAY_SCAN;
|
||||
flag3.fast_power_cycle_disable |= APP_DISABLE_POWERCYCLE;
|
||||
flag3.bootcount_update |= DEEPSLEEP_BOOTCOUNT;
|
||||
flag3.compatibility_check |= OTA_COMPATIBILITY;
|
||||
Settings.save_data = SAVE_DATA;
|
||||
Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY;
|
||||
Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; // SetOption36
|
||||
@ -739,7 +745,7 @@ void SettingsDefaultSet2(void)
|
||||
}
|
||||
|
||||
// Module
|
||||
// Settings.flag.interlock = 0;
|
||||
// flag.interlock |= 0;
|
||||
Settings.interlock[0] = 0xFF; // Legacy support using all relays in one interlock group
|
||||
Settings.module = MODULE;
|
||||
ModuleDefault(WEMOS);
|
||||
@ -751,7 +757,7 @@ void SettingsDefaultSet2(void)
|
||||
SettingsUpdateText(SET_OTAURL, PSTR(OTA_URL));
|
||||
|
||||
// Power
|
||||
Settings.flag.save_state = SAVE_STATE;
|
||||
flag.save_state |= SAVE_STATE;
|
||||
Settings.power = APP_POWER;
|
||||
Settings.poweronstate = APP_POWERON_STATE;
|
||||
Settings.blinktime = APP_BLINKTIME;
|
||||
@ -769,8 +775,8 @@ void SettingsDefaultSet2(void)
|
||||
Settings.seriallog_level = SERIAL_LOG_LEVEL;
|
||||
|
||||
// Wifi
|
||||
Settings.flag3.use_wifi_scan = WIFI_SCAN_AT_RESTART;
|
||||
Settings.flag3.use_wifi_rescan = WIFI_SCAN_REGULARLY;
|
||||
flag3.use_wifi_scan |= WIFI_SCAN_AT_RESTART;
|
||||
flag3.use_wifi_rescan |= WIFI_SCAN_REGULARLY;
|
||||
Settings.wifi_output_power = 170;
|
||||
Settings.param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL;
|
||||
ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS);
|
||||
@ -791,38 +797,38 @@ void SettingsDefaultSet2(void)
|
||||
Settings.syslog_level = SYS_LOG_LEVEL;
|
||||
|
||||
// Webserver
|
||||
Settings.flag2.emulation = EMULATION;
|
||||
Settings.flag3.gui_hostname_ip = GUI_SHOW_HOSTNAME;
|
||||
Settings.flag3.mdns_enabled = MDNS_ENABLED;
|
||||
flag2.emulation |= EMULATION;
|
||||
flag3.gui_hostname_ip |= GUI_SHOW_HOSTNAME;
|
||||
flag3.mdns_enabled |= MDNS_ENABLED;
|
||||
Settings.webserver = WEB_SERVER;
|
||||
Settings.weblog_level = WEB_LOG_LEVEL;
|
||||
SettingsUpdateText(SET_WEBPWD, PSTR(WEB_PASSWORD));
|
||||
SettingsUpdateText(SET_CORS, PSTR(CORS_DOMAIN));
|
||||
|
||||
// Button
|
||||
Settings.flag.button_restrict = KEY_DISABLE_MULTIPRESS;
|
||||
Settings.flag.button_swap = KEY_SWAP_DOUBLE_PRESS;
|
||||
Settings.flag.button_single = KEY_ONLY_SINGLE_PRESS;
|
||||
flag.button_restrict |= KEY_DISABLE_MULTIPRESS;
|
||||
flag.button_swap |= KEY_SWAP_DOUBLE_PRESS;
|
||||
flag.button_single |= KEY_ONLY_SINGLE_PRESS;
|
||||
Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; // Default 4 seconds hold time
|
||||
|
||||
// Switch
|
||||
for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; }
|
||||
|
||||
// MQTT
|
||||
Settings.flag.mqtt_enabled = MQTT_USE;
|
||||
Settings.flag.mqtt_response = MQTT_RESULT_COMMAND;
|
||||
Settings.flag.mqtt_offline = MQTT_LWT_MESSAGE;
|
||||
Settings.flag.mqtt_power_retain = MQTT_POWER_RETAIN;
|
||||
Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN;
|
||||
Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN;
|
||||
Settings.flag.mqtt_sensor_retain = MQTT_SENSOR_RETAIN;
|
||||
// Settings.flag.mqtt_serial = 0;
|
||||
Settings.flag.device_index_enable = MQTT_POWER_FORMAT;
|
||||
Settings.flag3.time_append_timezone = MQTT_APPEND_TIMEZONE;
|
||||
Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL;
|
||||
Settings.flag3.no_hold_retain = MQTT_NO_HOLD_RETAIN;
|
||||
Settings.flag3.use_underscore = MQTT_INDEX_SEPARATOR;
|
||||
Settings.flag3.grouptopic_mode = MQTT_GROUPTOPIC_FORMAT;
|
||||
flag.mqtt_enabled |= MQTT_USE;
|
||||
flag.mqtt_response |= MQTT_RESULT_COMMAND;
|
||||
flag.mqtt_offline |= MQTT_LWT_MESSAGE;
|
||||
flag.mqtt_power_retain |= MQTT_POWER_RETAIN;
|
||||
flag.mqtt_button_retain |= MQTT_BUTTON_RETAIN;
|
||||
flag.mqtt_switch_retain |= MQTT_SWITCH_RETAIN;
|
||||
flag.mqtt_sensor_retain |= MQTT_SENSOR_RETAIN;
|
||||
// flag.mqtt_serial |= 0;
|
||||
flag.device_index_enable |= MQTT_POWER_FORMAT;
|
||||
flag3.time_append_timezone |= MQTT_APPEND_TIMEZONE;
|
||||
flag3.button_switch_force_local |= MQTT_BUTTON_SWITCH_FORCE_LOCAL;
|
||||
flag3.no_hold_retain |= MQTT_NO_HOLD_RETAIN;
|
||||
flag3.use_underscore |= MQTT_INDEX_SEPARATOR;
|
||||
flag3.grouptopic_mode |= MQTT_GROUPTOPIC_FORMAT;
|
||||
SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST);
|
||||
Settings.mqtt_port = MQTT_PORT;
|
||||
SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID);
|
||||
@ -856,13 +862,13 @@ void SettingsDefaultSet2(void)
|
||||
Settings.mqttlog_level = MQTT_LOG_LEVEL;
|
||||
|
||||
// Energy
|
||||
Settings.flag.no_power_on_check = ENERGY_VOLTAGE_ALWAYS;
|
||||
Settings.flag2.current_resolution = 3;
|
||||
// Settings.flag2.voltage_resolution = 0;
|
||||
// Settings.flag2.wattage_resolution = 0;
|
||||
Settings.flag2.energy_resolution = ENERGY_RESOLUTION;
|
||||
Settings.flag3.dds2382_model = ENERGY_DDS2382_MODE;
|
||||
Settings.flag3.hardware_energy_total = ENERGY_HARDWARE_TOTALS;
|
||||
flag.no_power_on_check |= ENERGY_VOLTAGE_ALWAYS;
|
||||
flag2.current_resolution |= 3;
|
||||
// flag2.voltage_resolution |= 0;
|
||||
// flag2.wattage_resolution |= 0;
|
||||
flag2.energy_resolution |= ENERGY_RESOLUTION;
|
||||
flag3.dds2382_model |= ENERGY_DDS2382_MODE;
|
||||
flag3.hardware_energy_total |= ENERGY_HARDWARE_TOTALS;
|
||||
Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY;
|
||||
// Settings.energy_power_delta = 0;
|
||||
Settings.energy_power_calibration = HLW_PREF_PULSE;
|
||||
@ -892,12 +898,12 @@ void SettingsDefaultSet2(void)
|
||||
Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP;
|
||||
|
||||
// IRRemote
|
||||
Settings.flag.ir_receive_decimal = IR_DATA_RADIX;
|
||||
Settings.flag3.receive_raw = IR_ADD_RAW_DATA;
|
||||
flag.ir_receive_decimal |= IR_DATA_RADIX;
|
||||
flag3.receive_raw |= IR_ADD_RAW_DATA;
|
||||
Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE;
|
||||
|
||||
// RF Bridge
|
||||
Settings.flag.rf_receive_decimal = RF_DATA_RADIX;
|
||||
flag.rf_receive_decimal |= RF_DATA_RADIX;
|
||||
// for (uint32_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; }
|
||||
memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9);
|
||||
|
||||
@ -913,43 +919,43 @@ void SettingsDefaultSet2(void)
|
||||
// }
|
||||
|
||||
// Sensor
|
||||
Settings.flag.temperature_conversion = TEMP_CONVERSION;
|
||||
Settings.flag.pressure_conversion = PRESSURE_CONVERSION;
|
||||
Settings.flag2.pressure_resolution = PRESSURE_RESOLUTION;
|
||||
Settings.flag2.humidity_resolution = HUMIDITY_RESOLUTION;
|
||||
Settings.flag2.temperature_resolution = TEMP_RESOLUTION;
|
||||
Settings.flag3.ds18x20_internal_pullup = DS18X20_PULL_UP;
|
||||
Settings.flag3.counter_reset_on_tele = COUNTER_RESET;
|
||||
flag.temperature_conversion |= TEMP_CONVERSION;
|
||||
flag.pressure_conversion |= PRESSURE_CONVERSION;
|
||||
flag2.pressure_resolution |= PRESSURE_RESOLUTION;
|
||||
flag2.humidity_resolution |= HUMIDITY_RESOLUTION;
|
||||
flag2.temperature_resolution |= TEMP_RESOLUTION;
|
||||
flag3.ds18x20_internal_pullup |= DS18X20_PULL_UP;
|
||||
flag3.counter_reset_on_tele |= COUNTER_RESET;
|
||||
// Settings.altitude = 0;
|
||||
|
||||
// Rules
|
||||
// Settings.rule_enabled = 0;
|
||||
// Settings.rule_once = 0;
|
||||
// for (uint32_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; }
|
||||
Settings.flag2.calc_resolution = CALC_RESOLUTION;
|
||||
flag2.calc_resolution |= CALC_RESOLUTION;
|
||||
|
||||
// Timer
|
||||
Settings.flag3.timers_enable = TIMERS_ENABLED;
|
||||
flag3.timers_enable |= TIMERS_ENABLED;
|
||||
|
||||
// Home Assistant
|
||||
Settings.flag.hass_light = HASS_AS_LIGHT;
|
||||
Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE;
|
||||
Settings.flag3.hass_tele_on_power = TELE_ON_POWER;
|
||||
flag.hass_light |= HASS_AS_LIGHT;
|
||||
flag.hass_discovery |= HOME_ASSISTANT_DISCOVERY_ENABLE;
|
||||
flag3.hass_tele_on_power |= TELE_ON_POWER;
|
||||
|
||||
// Knx
|
||||
Settings.flag.knx_enabled = KNX_ENABLED;
|
||||
Settings.flag.knx_enable_enhancement = KNX_ENHANCED;
|
||||
flag.knx_enabled |= KNX_ENABLED;
|
||||
flag.knx_enable_enhancement |= KNX_ENHANCED;
|
||||
|
||||
// Light
|
||||
Settings.flag.pwm_control = LIGHT_MODE;
|
||||
Settings.flag.ws_clock_reverse = LIGHT_CLOCK_DIRECTION;
|
||||
Settings.flag.light_signal = LIGHT_PAIRS_CO2;
|
||||
Settings.flag.not_power_linked = LIGHT_POWER_CONTROL;
|
||||
Settings.flag.decimal_text = LIGHT_COLOR_RADIX;
|
||||
Settings.flag3.pwm_multi_channels = LIGHT_CHANNEL_MODE;
|
||||
Settings.flag3.slider_dimmer_stay_on = LIGHT_SLIDER_POWER;
|
||||
Settings.flag4.alexa_ct_range = LIGHT_ALEXA_CT_RANGE;
|
||||
Settings.flag4.pwm_ct_mode = LIGHT_PWM_CT_MODE;
|
||||
flag.pwm_control |= LIGHT_MODE;
|
||||
flag.ws_clock_reverse |= LIGHT_CLOCK_DIRECTION;
|
||||
flag.light_signal |= LIGHT_PAIRS_CO2;
|
||||
flag.not_power_linked |= LIGHT_POWER_CONTROL;
|
||||
flag.decimal_text |= LIGHT_COLOR_RADIX;
|
||||
flag3.pwm_multi_channels |= LIGHT_CHANNEL_MODE;
|
||||
flag3.slider_dimmer_stay_on |= LIGHT_SLIDER_POWER;
|
||||
flag4.alexa_ct_range |= LIGHT_ALEXA_CT_RANGE;
|
||||
flag4.pwm_ct_mode |= LIGHT_PWM_CT_MODE;
|
||||
|
||||
Settings.pwm_frequency = PWM_FREQ;
|
||||
Settings.pwm_range = PWM_RANGE;
|
||||
@ -1036,13 +1042,18 @@ void SettingsDefaultSet2(void)
|
||||
SettingsEnableAllI2cDrivers();
|
||||
|
||||
// Tuya
|
||||
Settings.flag3.tuya_apply_o20 = TUYA_SETOPTION_20;
|
||||
Settings.flag3.tuya_serial_mqtt_publish = MQTT_TUYA_RECEIVED;
|
||||
flag3.tuya_apply_o20 |= TUYA_SETOPTION_20;
|
||||
flag3.tuya_serial_mqtt_publish |= MQTT_TUYA_RECEIVED;
|
||||
|
||||
Settings.flag3.buzzer_enable = BUZZER_ENABLE;
|
||||
Settings.flag3.shutter_mode = SHUTTER_SUPPORT;
|
||||
Settings.flag3.pcf8574_ports_inverted = PCF8574_INVERT_PORTS;
|
||||
Settings.flag4.zigbee_use_names = ZIGBEE_FRIENDLY_NAMES;
|
||||
flag3.buzzer_enable |= BUZZER_ENABLE;
|
||||
flag3.shutter_mode |= SHUTTER_SUPPORT;
|
||||
flag3.pcf8574_ports_inverted |= PCF8574_INVERT_PORTS;
|
||||
flag4.zigbee_use_names |= ZIGBEE_FRIENDLY_NAMES;
|
||||
|
||||
Settings.flag = flag;
|
||||
Settings.flag2 = flag2;
|
||||
Settings.flag3 = flag3;
|
||||
Settings.flag4 = flag4;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
@ -554,10 +554,16 @@ void GetFeatures(void)
|
||||
#ifdef USE_PING
|
||||
feature6 |= 0x00000080; // xdrv_38_ping.ino
|
||||
#endif
|
||||
#ifdef USE_WINDMETER
|
||||
feature6 |= 0x00000100; // xsns_68_windmeter.ino
|
||||
#endif
|
||||
#ifdef USE_OPENTHERM
|
||||
feature6 |= 0x00000200; // xsns_69_opentherm.ino
|
||||
#endif
|
||||
#ifdef USE_THERMOSTAT
|
||||
feature6 |= 0x00000400; // xdrv_39_heating.ino
|
||||
#endif
|
||||
|
||||
// feature6 |= 0x00000100;
|
||||
// feature6 |= 0x00000200;
|
||||
// feature6 |= 0x00000400;
|
||||
// feature6 |= 0x00000800;
|
||||
|
||||
// feature6 |= 0x00001000;
|
||||
@ -583,6 +589,7 @@ void GetFeatures(void)
|
||||
// feature6 |= 0x10000000;
|
||||
// feature6 |= 0x20000000;
|
||||
// feature6 |= 0x40000000;
|
||||
// feature6 |= 0x80000000;
|
||||
|
||||
#ifdef USE_WEBCAM
|
||||
feature6 |= 0x80000000;
|
||||
#endif
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ const uint8_t MAX_XDRV_DRIVERS = 96; // Max number of allowed driver driv
|
||||
const uint8_t MAX_XSNS_DRIVERS = 96; // Max number of allowed sensor drivers
|
||||
const uint8_t MAX_I2C_DRIVERS = 96; // Max number of allowed i2c drivers
|
||||
const uint8_t MAX_SHUTTERS = 4; // Max number of shutters
|
||||
const uint8_t MAX_PCF8574 = 8; // Max number of PCF8574 devices
|
||||
const uint8_t MAX_PCF8574 = 4; // Max number of PCF8574 devices
|
||||
const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters
|
||||
const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules
|
||||
|
||||
@ -319,9 +319,9 @@ enum DevGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE
|
||||
|
||||
enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER,
|
||||
SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER,
|
||||
SRC_MAX };
|
||||
SRC_THERMOSTAT, SRC_MAX };
|
||||
const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|"
|
||||
"Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter";
|
||||
"Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter|Thermostat";
|
||||
|
||||
const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 };
|
||||
|
||||
|
@ -183,6 +183,7 @@
|
||||
#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code)
|
||||
//#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code)
|
||||
//#define USE_ARDUINO_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
|
||||
//#define USE_WINDMETER // Add support for analog anemometer
|
||||
#undef DEBUG_THEO // Disable debug code
|
||||
#undef USE_DEBUG_DRIVER // Disable debug code
|
||||
#endif // FIRMWARE_SENSORS
|
||||
|
@ -230,6 +230,9 @@ enum UserSelectablePins {
|
||||
GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX
|
||||
GPIO_AS3935,
|
||||
GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface
|
||||
GPIO_BOILER_OT_RX, // OpenTherm Boiler RX pin
|
||||
GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin
|
||||
GPIO_WINDMETER_SPEED, // WindMeter speed counter pin
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
// Programmer selectable GPIO functionality
|
||||
@ -317,7 +320,9 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_CC1101_GDO0 "|" D_SENSOR_CC1101_GDO2 "|"
|
||||
D_SENSOR_HRXL_RX "|"
|
||||
D_SENSOR_ELECTRIQ_MOODL "|"
|
||||
D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX
|
||||
D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX "|"
|
||||
D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|"
|
||||
D_SENSOR_WINDMETER_SPEED
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
@ -591,6 +596,9 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
||||
#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR)
|
||||
GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin
|
||||
#endif
|
||||
#ifdef USE_WINDMETER
|
||||
GPIO_WINDMETER_SPEED,
|
||||
#endif
|
||||
#ifdef USE_MP3_PLAYER
|
||||
GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface
|
||||
#endif
|
||||
@ -612,16 +620,20 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
||||
GPIO_RDM6300_RX,
|
||||
#endif
|
||||
#ifdef USE_IBEACON
|
||||
GPIO_IBEACON_RX,
|
||||
GPIO_IBEACON_TX,
|
||||
GPIO_IBEACON_RX,
|
||||
#endif
|
||||
#ifdef USE_GPS
|
||||
GPIO_GPS_RX, // GPS serial interface
|
||||
GPIO_GPS_TX, // GPS serial interface
|
||||
GPIO_GPS_RX, // GPS serial interface
|
||||
#endif
|
||||
#ifdef USE_HM10
|
||||
GPIO_HM10_RX, // GPS serial interface
|
||||
GPIO_HM10_TX, // GPS serial interface
|
||||
GPIO_HM10_RX, // GPS serial interface
|
||||
#endif
|
||||
#ifdef USE_OPENTHERM
|
||||
GPIO_BOILER_OT_TX,
|
||||
GPIO_BOILER_OT_RX,
|
||||
#endif
|
||||
|
||||
#ifdef USE_MGC3130
|
||||
|
@ -125,6 +125,8 @@ enum UserSelectablePins {
|
||||
GPIO_WEBCAM_PSCLK,
|
||||
GPIO_WEBCAM_HSD,
|
||||
GPIO_WEBCAM_PSRCS,
|
||||
GPIO_BOILER_OT_RX, GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin
|
||||
GPIO_WINDMETER_SPEED, // WindMeter speed counter pin
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
enum ProgramSelectablePins {
|
||||
@ -136,22 +138,22 @@ enum ProgramSelectablePins {
|
||||
// Text in webpage Module Parameters and commands GPIOS and GPIO
|
||||
const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_NONE "|"
|
||||
D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "n|" D_SENSOR_BUTTON "i|" D_SENSOR_BUTTON "in|"
|
||||
D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "n|"
|
||||
D_SENSOR_RELAY "|" D_SENSOR_RELAY "i|"
|
||||
D_SENSOR_LED "|" D_SENSOR_LED "i|"
|
||||
D_SENSOR_COUNTER "|" D_SENSOR_COUNTER "n|"
|
||||
D_SENSOR_PWM "|" D_SENSOR_PWM "i|"
|
||||
D_SENSOR_BUZZER "|" D_SENSOR_BUZZER "i|"
|
||||
D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|"
|
||||
D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "_n|" D_SENSOR_BUTTON "_i|" D_SENSOR_BUTTON "_in|"
|
||||
D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "_n|"
|
||||
D_SENSOR_RELAY "|" D_SENSOR_RELAY "_i|"
|
||||
D_SENSOR_LED "|" D_SENSOR_LED "_i|"
|
||||
D_SENSOR_COUNTER "|" D_SENSOR_COUNTER "_n|"
|
||||
D_SENSOR_PWM "|" D_SENSOR_PWM "_i|"
|
||||
D_SENSOR_BUZZER "|" D_SENSOR_BUZZER "_i|"
|
||||
D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "_i|"
|
||||
D_SENSOR_I2C_SCL "|" D_SENSOR_I2C_SDA "|"
|
||||
D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|"
|
||||
D_SENSOR_SSPI_MISO "|" D_SENSOR_SSPI_MOSI "|" D_SENSOR_SSPI_SCLK "|" D_SENSOR_SSPI_CS "|" D_SENSOR_SSPI_DC "|"
|
||||
D_SENSOR_BACKLIGHT "|" D_SENSOR_OLED_RESET "|"
|
||||
D_SENSOR_IRSEND "|" D_SENSOR_IRRECV "|"
|
||||
D_SENSOR_RFSEND "|" D_SENSOR_RFRECV "|"
|
||||
D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" D_SENSOR_DHT11 "o|"
|
||||
D_SENSOR_DS18X20 "|" D_SENSOR_DS18X20 "o|"
|
||||
D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" D_SENSOR_DHT11 "_o|"
|
||||
D_SENSOR_DS18X20 "|" D_SENSOR_DS18X20 "_o|"
|
||||
D_SENSOR_WS2812 "|"
|
||||
D_SENSOR_MHZ_TX "|" D_SENSOR_MHZ_RX "|"
|
||||
D_SENSOR_PZEM0XX_TX "|" D_SENSOR_PZEM004_RX "|" D_SENSOR_PZEM016_RX "|" D_SENSOR_PZEM017_RX "|"
|
||||
@ -171,7 +173,7 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_RF_SENSOR "|"
|
||||
D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX "|"
|
||||
D_SENSOR_MAX31855_CS "|" D_SENSOR_MAX31855_CLK "|" D_SENSOR_MAX31855_DO "|"
|
||||
D_SENSOR_NRG_SEL "|" D_SENSOR_NRG_SEL "i|" D_SENSOR_NRG_CF1 "|" D_SENSOR_HLW_CF "|" D_SENSOR_HJL_CF "|"
|
||||
D_SENSOR_NRG_SEL "|" D_SENSOR_NRG_SEL "_i|" D_SENSOR_NRG_CF1 "|" D_SENSOR_HLW_CF "|" D_SENSOR_HJL_CF "|"
|
||||
D_SENSOR_MCP39F5_TX "|" D_SENSOR_MCP39F5_RX "|" D_SENSOR_MCP39F5_RST "|"
|
||||
D_SENSOR_PN532_TX "|" D_SENSOR_PN532_RX "|"
|
||||
D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT "|" D_SENSOR_SM16716_POWER "|"
|
||||
@ -179,7 +181,7 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|"
|
||||
D_SENSOR_ARIRFRCV "|" D_SENSOR_ARIRFSEL "|"
|
||||
D_SENSOR_TXD "|" D_SENSOR_RXD "|"
|
||||
D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|"
|
||||
D_SENSOR_ROTARY "_1a|" D_SENSOR_ROTARY "_1b|" D_SENSOR_ROTARY "_2a|" D_SENSOR_ROTARY "_2b|"
|
||||
D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|"
|
||||
D_SENSOR_ADE7953_IRQ "|"
|
||||
D_SENSOR_SOLAXX1_TX "|" D_SENSOR_SOLAXX1_RX "|"
|
||||
@ -191,7 +193,7 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_DDSU666_TX "|" D_SENSOR_DDSU666_RX "|"
|
||||
D_SENSOR_SM2135_CLK "|" D_SENSOR_SM2135_DAT "|"
|
||||
D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|"
|
||||
D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "i|"
|
||||
D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "_i|"
|
||||
D_SENSOR_HPMA_RX "|" D_SENSOR_HPMA_TX "|"
|
||||
D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX "|"
|
||||
D_SENSOR_HM10_RX "|" D_SENSOR_HM10_TX "|"
|
||||
@ -202,7 +204,7 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_AS3935 "|"
|
||||
D_ANALOG_INPUT "|"
|
||||
D_TEMPERATURE "|" D_LIGHT "|"
|
||||
D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|"
|
||||
D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "_i|"
|
||||
D_RANGE "|"
|
||||
D_CT_POWER "|"
|
||||
D_GPIO_WEBCAM_PWDN "|" D_GPIO_WEBCAM_RESET "|" D_GPIO_WEBCAM_XCLK "|"
|
||||
@ -211,7 +213,9 @@ const char kSensorNames[] PROGMEM =
|
||||
D_GPIO_WEBCAM_VSYNC "|" D_GPIO_WEBCAM_HREF "|" D_GPIO_WEBCAM_PCLK "|"
|
||||
D_GPIO_WEBCAM_PSCLK "|"
|
||||
D_GPIO_WEBCAM_HSD "|"
|
||||
D_GPIO_WEBCAM_PSRCS
|
||||
D_GPIO_WEBCAM_PSRCS "|"
|
||||
D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|"
|
||||
D_SENSOR_WINDMETER_SPEED
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
@ -395,8 +399,8 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
||||
AGPIO(GPIO_SOLAXX1_RX), // Solax Inverter rx pin
|
||||
#endif // USE_SOLAX_X1
|
||||
#ifdef USE_LE01MR
|
||||
AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin
|
||||
AGPIO(GPIO_LE01MR_TX), // F7F LE-01MR energy meter tx pin
|
||||
AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin
|
||||
#endif // IFDEF:USE_LE01MR
|
||||
#endif // USE_ENERGY_SENSOR
|
||||
|
||||
@ -432,6 +436,9 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
||||
#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR)
|
||||
AGPIO(GPIO_TX2X_TXD_BLACK), // TX20/TX23 Transmission Pin
|
||||
#endif
|
||||
#ifdef USE_WINDMETER
|
||||
GPIO_WINDMETER_SPEED,
|
||||
#endif
|
||||
#ifdef USE_MP3_PLAYER
|
||||
AGPIO(GPIO_MP3_DFR562), // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface
|
||||
#endif
|
||||
@ -453,16 +460,20 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
||||
AGPIO(GPIO_RDM6300_RX),
|
||||
#endif
|
||||
#ifdef USE_IBEACON
|
||||
AGPIO(GPIO_IBEACON_RX),
|
||||
AGPIO(GPIO_IBEACON_TX),
|
||||
AGPIO(GPIO_IBEACON_RX),
|
||||
#endif
|
||||
#ifdef USE_GPS
|
||||
AGPIO(GPIO_GPS_RX), // GPS serial interface
|
||||
AGPIO(GPIO_GPS_TX), // GPS serial interface
|
||||
AGPIO(GPIO_GPS_RX), // GPS serial interface
|
||||
#endif
|
||||
#ifdef USE_HM10
|
||||
AGPIO(GPIO_HM10_RX), // GPS serial interface
|
||||
AGPIO(GPIO_HM10_TX), // GPS serial interface
|
||||
AGPIO(GPIO_HM10_RX), // GPS serial interface
|
||||
#endif
|
||||
#ifdef USE_OPENTHERM
|
||||
GPIO_BOILER_OT_TX,
|
||||
GPIO_BOILER_OT_RX,
|
||||
#endif
|
||||
|
||||
#ifdef USE_MGC3130
|
||||
@ -523,29 +534,12 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
||||
AGPIO(GPIO_WEBCAM_XCLK),
|
||||
AGPIO(GPIO_WEBCAM_SIOD),
|
||||
AGPIO(GPIO_WEBCAM_SIOC),
|
||||
|
||||
// AGPIO(GPIO_WEBCAM_Y9),
|
||||
// AGPIO(GPIO_WEBCAM_Y8),
|
||||
// AGPIO(GPIO_WEBCAM_Y7),
|
||||
// AGPIO(GPIO_WEBCAM_Y6),
|
||||
// AGPIO(GPIO_WEBCAM_Y5),
|
||||
// AGPIO(GPIO_WEBCAM_Y4),
|
||||
// AGPIO(GPIO_WEBCAM_Y3),
|
||||
// AGPIO(GPIO_WEBCAM_Y2),
|
||||
|
||||
AGPIO(GPIO_WEBCAM_DATA) + MAX_WEBCAM_DATA,
|
||||
|
||||
AGPIO(GPIO_WEBCAM_VSYNC),
|
||||
AGPIO(GPIO_WEBCAM_HREF),
|
||||
AGPIO(GPIO_WEBCAM_PCLK),
|
||||
AGPIO(GPIO_WEBCAM_PSCLK),
|
||||
|
||||
// AGPIO(GPIO_WEBCAM_HSD1),
|
||||
// AGPIO(GPIO_WEBCAM_HSD2),
|
||||
// AGPIO(GPIO_WEBCAM_HSD3),
|
||||
|
||||
AGPIO(GPIO_WEBCAM_HSD) + MAX_WEBCAM_HSD,
|
||||
|
||||
AGPIO(GPIO_WEBCAM_PSRCS),
|
||||
#endif
|
||||
};
|
||||
|
@ -230,6 +230,11 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM =
|
||||
const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM =
|
||||
"}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace
|
||||
|
||||
const char HTTP_MODULE_TEMPLATE_REPLACE_INDEX[] PROGMEM =
|
||||
"}2%d'>%s (%d)}3"; // }2 and }3 are used in below os.replace
|
||||
const char HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX[] PROGMEM =
|
||||
"}2%d'>%s}3"; // }2 and }3 are used in below os.replace
|
||||
|
||||
const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM =
|
||||
#ifdef ESP8266
|
||||
"var os;"
|
||||
@ -257,7 +262,7 @@ const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM =
|
||||
"t.style.visibility=(b>0)?'':'hidden';"
|
||||
"}"
|
||||
"function sk(s,g){" // s = value, g = id and name
|
||||
"var o=os.replace(/}2/g,\"<option value='\").replace(/}3/g,\")</option>\");"
|
||||
"var o=os.replace(/}2/g,\"<option value='\").replace(/}3/g,\"</option>\");"
|
||||
"eb('g'+g).innerHTML=o;"
|
||||
"eb('g'+g).value=(g<99)?s&0xffe0:s;"
|
||||
"if(g<99){ot(g,s);}"
|
||||
@ -1452,17 +1457,21 @@ void HandleTemplateConfiguration(void)
|
||||
|
||||
WSContentSend_P(HTTP_SCRIPT_TEMPLATE);
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3...
|
||||
#ifdef ESP8266
|
||||
if (1 == i) {
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, AGPIO(GPIO_USER), D_SENSOR_USER, AGPIO(GPIO_USER)); // }2'255'>User (255)}3
|
||||
}
|
||||
#ifdef ESP8266
|
||||
uint32_t midx = pgm_read_byte(kGpioNiceList + i);
|
||||
uint32_t ridx = midx;
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), ridx);
|
||||
#else // ESP32
|
||||
if (1 == i) {
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); // }2'255'>User}3
|
||||
}
|
||||
uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0;
|
||||
uint32_t midx = ridx >> 5;
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames));
|
||||
#endif // ESP8266 - ESP32
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), ridx);
|
||||
}
|
||||
WSContentSend_P(PSTR("\";"));
|
||||
|
||||
@ -1491,12 +1500,16 @@ void HandleTemplateConfiguration(void)
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, i, GetTextIndexed(stemp, sizeof(stemp), i, kAdc0Names), i);
|
||||
}
|
||||
WSContentSend_P(HTTP_SCRIPT_TEMPLATE3);
|
||||
#endif
|
||||
#endif // ESP8266
|
||||
|
||||
WSContentSend_P(HTTP_SCRIPT_TEMPLATE4);
|
||||
for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { // "}2'%d'>%s (%d)}3" - "}2'0'>Sonoff Basic (1)}3"
|
||||
uint32_t midx = pgm_read_byte(kModuleNiceList + i);
|
||||
#ifdef ESP8266
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1);
|
||||
#else // ESP32
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_INDEX, midx, AnyModuleName(midx).c_str(), midx +1);
|
||||
#endif // ESP8266 - ESP32
|
||||
}
|
||||
WSContentSend_P(HTTP_SCRIPT_TEMPLATE5);
|
||||
|
||||
@ -1617,20 +1630,27 @@ void HandleModuleConfiguration(void)
|
||||
midx = pgm_read_byte(kModuleNiceList + i -1);
|
||||
vidx = midx +1;
|
||||
}
|
||||
#ifdef ESP8266
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx);
|
||||
#else // ESP32
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_INDEX, midx, AnyModuleName(midx).c_str(), vidx);
|
||||
#endif // ESP8266 - ESP32
|
||||
}
|
||||
WSContentSend_P(PSTR("\";sk(%d,99);os=\""), Settings.module);
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) {
|
||||
#ifdef ESP8266
|
||||
midx = pgm_read_byte(kGpioNiceList + i);
|
||||
uint32_t ridx = midx;
|
||||
#else // ESP32
|
||||
uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0;
|
||||
midx = ridx >> 5;
|
||||
#endif // ESP8266 - ESP32
|
||||
if (!GetUsedInModule(midx, cmodule.io)) {
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), ridx);
|
||||
}
|
||||
#else // ESP32
|
||||
uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0;
|
||||
midx = ridx >> 5;
|
||||
if (!GetUsedInModule(midx, cmodule.io)) {
|
||||
WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames));
|
||||
}
|
||||
#endif // ESP8266 - ESP32
|
||||
}
|
||||
WSContentSend_P(PSTR("\";"));
|
||||
|
||||
|
@ -110,7 +110,7 @@ void DomoticzUpdateFanState(void)
|
||||
void MqttPublishDomoticzPowerState(uint8_t device)
|
||||
{
|
||||
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
|
||||
if ((device < 1) || (device > devices_present)) { device = 1; }
|
||||
if ((device < 1) || (device > devices_present) || (device > MAX_DOMOTICZ_IDX)) { device = 1; }
|
||||
if (Settings.domoticz_relay_idx[device -1]) {
|
||||
#ifdef USE_SHUTTER
|
||||
if (domoticz_is_shutter) {
|
||||
|
@ -1181,6 +1181,28 @@ chknext:
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef ESP32
|
||||
if (!strncmp(vname,"core",4)) {
|
||||
fvar=xPortGetCoreID();
|
||||
goto exit;
|
||||
}
|
||||
#ifdef USE_SCRIPT_TASK
|
||||
if (!strncmp(vname,"ct(",3)) {
|
||||
lp+=3;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
|
||||
while (*lp==' ') lp++;
|
||||
float fvar1;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
|
||||
while (*lp==' ') lp++;
|
||||
float fvar2;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
|
||||
lp++;
|
||||
fvar=scripter_create_task(fvar,fvar1,fvar2);
|
||||
len=0;
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
case 'd':
|
||||
if (!strncmp(vname,"day",3)) {
|
||||
@ -1705,6 +1727,12 @@ chknext:
|
||||
fvar=999;
|
||||
goto exit;
|
||||
}
|
||||
#ifdef ESP32
|
||||
if (!strncmp(vname,"pheap",5)) {
|
||||
fvar=ESP.getFreePsram();
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
if (!strncmp(vname,"prefix1",7)) {
|
||||
if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX1),glob_script_mem.max_ssize);
|
||||
goto strexit;
|
||||
@ -5087,6 +5115,47 @@ bool RulesProcessEvent(char *json_event) {
|
||||
if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,json_event);
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
#ifdef USE_SCRIPT_TASK
|
||||
uint16_t task_timer1;
|
||||
uint16_t task_timer2;
|
||||
|
||||
void script_task1(void *arg) {
|
||||
while (1) {
|
||||
delay(task_timer1);
|
||||
Run_Scripter(">t1",3,0);
|
||||
}
|
||||
}
|
||||
|
||||
void script_task2(void *arg) {
|
||||
while (1) {
|
||||
delay(task_timer2);
|
||||
Run_Scripter(">t2",3,0);
|
||||
}
|
||||
}
|
||||
#ifndef STASK_STACK
|
||||
#define STASK_STACK 4096
|
||||
#endif
|
||||
|
||||
#ifndef STASK_PRIO
|
||||
#define STASK_PRIO 5
|
||||
#endif
|
||||
|
||||
uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) {
|
||||
//return 0;
|
||||
BaseType_t res=0;
|
||||
if (core>1) {core = 1;}
|
||||
if (num == 1) {
|
||||
res = xTaskCreatePinnedToCore(script_task1, "T 1", STASK_STACK, NULL, STASK_PRIO, NULL, core);
|
||||
task_timer1=time;
|
||||
} else {
|
||||
res = xTaskCreatePinnedToCore(script_task2, "T 2", STASK_STACK, NULL, STASK_PRIO, NULL, core);
|
||||
task_timer2=time;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif // USE_SCRIPT_TASK
|
||||
#endif // ESP32
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
@ -5235,7 +5304,9 @@ bool Xdrv10(uint8_t function)
|
||||
break;
|
||||
#ifdef USE_SCRIPT_WEB_DISPLAY
|
||||
case FUNC_WEB_ADD_MAIN_BUTTON:
|
||||
ScriptWebShow('&');
|
||||
if (bitRead(Settings.rule_enabled, 0)) {
|
||||
ScriptWebShow('&');
|
||||
}
|
||||
break;
|
||||
#endif // USE_SCRIPT_WEB_DISPLAY
|
||||
case FUNC_WEB_ADD_HANDLER:
|
||||
|
@ -28,12 +28,14 @@ const char kHAssJsonSensorTypes[] PROGMEM =
|
||||
D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|"
|
||||
D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY "|"
|
||||
D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|";
|
||||
|
||||
const char kHAssJsonSensorUnits[] PROGMEM =
|
||||
"||||"
|
||||
"VA|%|A|Cm|Hz|%|LX|"
|
||||
"%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W|"
|
||||
"VAr|kWh|kWh|V|Kg|kWh|"
|
||||
"ppm|ppm|ppb|";
|
||||
|
||||
const char kHAssJsonSensorDevCla[] PROGMEM =
|
||||
"dev_cla\":\"temperature|ic\":\"mdi:weather-rainy|dev_cla\":\"pressure|dev_cla\":\"pressure|"
|
||||
"dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|"
|
||||
@ -74,12 +76,12 @@ const char HASS_DISCOVER_BIN_PIR[] PROGMEM =
|
||||
"\"pl_on\":\"%s\"," // ON
|
||||
"\"off_dly\":1"; // Switchmode13 and Switchmode14 doesn't transmit an OFF state.
|
||||
|
||||
const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM =
|
||||
const char HASS_DISCOVER_BASE_LIGHT[] PROGMEM =
|
||||
",\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer
|
||||
"\"bri_stat_t\":\"%s\"," // stat/led2/RESULT
|
||||
"\"bri_scl\":100," // 100%
|
||||
"\"on_cmd_type\":\"%s\"," // power on (first), power on (last), no power on (brightness)
|
||||
"\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\"";
|
||||
"\"bri_val_tpl\":\"{{value_json.%s}}\"";
|
||||
|
||||
const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM =
|
||||
",\"rgb_cmd_t\":\"%s2\"," // cmnd/led2/Color2
|
||||
@ -138,6 +140,16 @@ const char kHAssTriggerTypeButtons[] PROGMEM =
|
||||
const char kHAssTriggerStringButtons[] PROGMEM =
|
||||
"|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|HOLD|";
|
||||
|
||||
const char kHAssError1[] PROGMEM =
|
||||
"HASS: MQTT discovery failed due to too long topic or friendly name."
|
||||
"Please shorten topic and friendly name. Failed to format";
|
||||
|
||||
const char kHAssError2[] PROGMEM =
|
||||
"HASS: The configuration of the Relays is wrong, there is a Light that is using an index lower than the number of the selected relay.\n "
|
||||
"The Relays have priority over the Lights, an incorrect order could lead to an erroneous Light control.\n "
|
||||
"Please update your configuration to avoid inconsistent results.\n "
|
||||
"Entities for Relays and Lights will not be available in Home Assistant until the configuration will be updated.";
|
||||
|
||||
uint8_t hass_init_step = 0;
|
||||
uint8_t hass_mode = 0;
|
||||
int hass_tele_period = 0;
|
||||
@ -153,9 +165,7 @@ void TryResponseAppend_P(const char *format, ...)
|
||||
int slen = sizeof(mqtt_data) - 1 - mlen;
|
||||
if (dlen >= slen)
|
||||
{
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: MQTT discovery failed due to too long topic or friendly name. "
|
||||
"Please shorten topic and friendly name. Failed to format(%u/%u):"),
|
||||
dlen, slen);
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s (%u/%u):"), kHAssError1, dlen, slen);
|
||||
va_start(args, format);
|
||||
vsnprintf_P(log_data, sizeof(log_data), format, args);
|
||||
AddLog(LOG_LEVEL_ERROR);
|
||||
@ -175,13 +185,33 @@ void HAssAnnounceRelayLight(void)
|
||||
char stemp2[TOPSZ];
|
||||
char stemp3[TOPSZ];
|
||||
char unique_id[30];
|
||||
bool is_light = false;
|
||||
bool is_topic_light = false;
|
||||
|
||||
bool LightControl = light_controller.isCTRGBLinked(); // SetOption37 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels
|
||||
bool PwmMulti = Settings.flag3.pwm_multi_channels; // SetOption68 - Multi-channel PWM instead of a single light
|
||||
bool is_topic_light = false; // Switch HAss domain between Lights and Relays
|
||||
bool ind_light = false; // Controls Separated Lights when SetOption37 is >= 128
|
||||
bool ct_light = false; // Controls a CT Light when SetOption37 is >= 128
|
||||
bool wt_light = false; // Controls a White Light when SetOption37 is >= 128
|
||||
bool err_flag = false; // When true it blocks the creation of entities if the order of the Relays is not correct to avoid issue with Lights
|
||||
|
||||
uint8_t dimmer = 1;
|
||||
uint8_t max_lights = 1;
|
||||
|
||||
// If there is a special Light to be enabled and managed with SetOption68 or SetOption37 >= 128, Discovery calculates the maximum number of entities to be generated in advance
|
||||
|
||||
if (PwmMulti) { max_lights = Light.subtype; }
|
||||
|
||||
if (!LightControl) {
|
||||
ind_light = 1;
|
||||
if (!PwmMulti) { max_lights = 2;}
|
||||
}
|
||||
|
||||
// Lights types: 0 = LST_NONE / 1 = LST_SINGLE / 2 = LST_COLDWARM / 3 = LST_RGB / 4 = LST_RGBW / 5 = LST_RGBCW
|
||||
|
||||
for (uint32_t i = 1; i <= MAX_RELAYS; i++)
|
||||
{
|
||||
is_light = ((i == devices_present) && (light_type));
|
||||
is_topic_light = Settings.flag.hass_light || is_light; // SetOption30 - Enforce HAss autodiscovery as light
|
||||
bool RelayX = PinUsed(GPIO_REL1 +i-1);
|
||||
is_topic_light = Settings.flag.hass_light && RelayX || light_type && !RelayX; // SetOption30 - Enforce HAss autodiscovery as light
|
||||
|
||||
mqtt_data[0] = '\0'; // Clear retained message
|
||||
|
||||
@ -195,70 +225,95 @@ void HAssAnnounceRelayLight(void)
|
||||
snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"),
|
||||
(is_topic_light) ? "light" : "switch", unique_id);
|
||||
|
||||
if (Settings.flag.hass_discovery && (i <= devices_present))
|
||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||
char name[33 + 2]; // friendlyname(33) + " " + index
|
||||
char value_template[33];
|
||||
char prefix[TOPSZ];
|
||||
char *command_topic = stemp1;
|
||||
char *state_topic = stemp2;
|
||||
char *availability_topic = stemp3;
|
||||
if ((i < Light.device) && !RelayX) {
|
||||
err_flag = true;
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s"), kHAssError2);
|
||||
} else {
|
||||
if (Settings.flag.hass_discovery && (RelayX || (Light.device > 0) && (max_lights > 0)) && !err_flag )
|
||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||
char name[33 + 2]; // friendlyname(33) + " " + index
|
||||
char value_template[33];
|
||||
char prefix[TOPSZ];
|
||||
char *command_topic = stemp1;
|
||||
char *state_topic = stemp2;
|
||||
char *availability_topic = stemp3;
|
||||
|
||||
if (i > MAX_FRIENDLYNAMES) {
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i);
|
||||
} else {
|
||||
snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i - 1));
|
||||
if (i > MAX_FRIENDLYNAMES) {
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i-1);
|
||||
} else {
|
||||
snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i-1));
|
||||
}
|
||||
|
||||
GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
|
||||
GetTopic_P(command_topic, CMND, mqtt_topic, value_template);
|
||||
GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2));
|
||||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId());
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
if ((i >= Light.device)
|
||||
#ifdef ESP8266
|
||||
|| PWM_DIMMER == my_module_type
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (!RelayX) {
|
||||
char *brightness_command_topic = stemp1;
|
||||
strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes
|
||||
char channel_num[9];
|
||||
if (PwmMulti) { // SetOption68 - Multi-channel PWM instead of a single light
|
||||
snprintf_P(channel_num, sizeof(channel_num), PSTR("Channel%d"), i);
|
||||
} else {
|
||||
if (!LightControl) { // SetOption37 >= 128 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels
|
||||
snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER "%d"), dimmer);
|
||||
dimmer ++;
|
||||
} else {
|
||||
snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER ""));
|
||||
}
|
||||
}
|
||||
GetTopic_P(brightness_command_topic, CMND, mqtt_topic, channel_num);
|
||||
TryResponseAppend_P(HASS_DISCOVER_BASE_LIGHT, brightness_command_topic, state_topic, stemp3, channel_num);
|
||||
}
|
||||
if ((ind_light && !PwmMulti) || LightControl) {
|
||||
|
||||
if (Light.subtype >= LST_RGB) {
|
||||
char *rgb_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic);
|
||||
|
||||
char *effect_command_topic = stemp1;
|
||||
GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic);
|
||||
}
|
||||
if (LST_RGBW == Light.subtype) { wt_light = true; }
|
||||
if (LST_RGBCW == Light.subtype) { ct_light = true; }
|
||||
}
|
||||
|
||||
if ((!ind_light && ct_light) || (LST_COLDWARM == Light.subtype &&
|
||||
!PwmMulti && LightControl)) {
|
||||
char *color_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic);
|
||||
ct_light = false;
|
||||
}
|
||||
if ((!ind_light && wt_light) || (LST_RGBW == Light.subtype &&
|
||||
!PwmMulti && LightControl)) {
|
||||
char *white_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic);
|
||||
wt_light = false;
|
||||
}
|
||||
ind_light = false;
|
||||
max_lights--;
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
TryResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
|
||||
GetTopic_P(command_topic, CMND, mqtt_topic, value_template);
|
||||
GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2));
|
||||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId());
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
if (is_light
|
||||
#ifdef ESP8266
|
||||
|| PWM_DIMMER == my_module_type
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char *brightness_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER);
|
||||
strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3);
|
||||
|
||||
if (Light.subtype >= LST_RGB)
|
||||
{
|
||||
char *rgb_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic);
|
||||
|
||||
char *effect_command_topic = stemp1;
|
||||
GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic);
|
||||
}
|
||||
if (LST_RGBW == Light.subtype)
|
||||
{
|
||||
char *white_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic);
|
||||
}
|
||||
if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype))
|
||||
{
|
||||
char *color_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic);
|
||||
}
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
TryResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
MqttPublish(stopic, true);
|
||||
}
|
||||
@ -346,7 +401,7 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint
|
||||
GetTopic_P(state_topic, STAT, mqtt_topic, jsoname);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
|
||||
snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), SettingsText(SET_FRIENDLYNAME1), device + 1);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), ModuleName().c_str(), device + 1);
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
if (!pir) {
|
||||
TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1));
|
||||
@ -372,7 +427,7 @@ void HAssAnnounceSwitches(void)
|
||||
|
||||
if (PinUsed(GPIO_SWT1, switch_index)) { switch_present = 1; }
|
||||
|
||||
if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) // Enable Discovery for Switches only if Switchtopic is set to a custom name
|
||||
if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) // Enable Discovery for Switches only if SwitchTopic is set to a custom name
|
||||
{
|
||||
|
||||
// switch matrix for triggers and binary sensor generation when switchtopic is set as custom (default index is 0,0 - TOGGLE, TOGGLE):
|
||||
@ -436,7 +491,6 @@ void HAssAnnounceSwitches(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HAssAnnounceButtons(void)
|
||||
{
|
||||
for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++)
|
||||
@ -469,7 +523,7 @@ void HAssAnnounceButtons(void)
|
||||
|
||||
// Trigger types: 10 = button_short_press | 11 = button_double_press | 12 = button_triple_press | 13 = button_quadruple_press | 14 = button_quintuple_press | 3 = button_long_press
|
||||
|
||||
if (!Settings.flag3.mqtt_buttons) { // Enable buttons discovery [SetOption73] - Decouple button from relay and send just mqtt topic
|
||||
if (!Settings.flag3.mqtt_buttons) { // Enable Buttons for discovery [SetOption73] - Decouple button from relay and send just mqtt topic
|
||||
button_present = 0;
|
||||
} else {
|
||||
if (Settings.flag.button_single) { // [SetOption13] Immediate action on button press, just SINGLE trigger
|
||||
@ -504,7 +558,7 @@ void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const
|
||||
char *availability_topic = stemp2;
|
||||
|
||||
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR));
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, MultiSubName);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), ModuleName().c_str(), sensorname, MultiSubName);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
@ -575,7 +629,6 @@ void HAssAnnounceSensors(void)
|
||||
snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); // {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}}
|
||||
// USE THE FOLLOWING LINE TO TEST JSON
|
||||
//snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"HX711\":{\"Weight\":[22,34,1023.4]}}"));
|
||||
//snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"TX23\":{\"Speed\":{\"Act\":8.6,\"Avg\":8.2,\"Min\":0,\"Max\":15.8},\"Dir\":{\"Card\":\"SSO\",\"Deg\":157.5,\"Avg\":145.5,\"AvgCard\":\"SO\",\"Min\":112.5,\"Max\":292.5,\"Range\":180}}}"));
|
||||
|
||||
StaticJsonBuffer<500> jsonBuffer;
|
||||
JsonObject &root = jsonBuffer.parseObject(sensordata);
|
||||
@ -659,16 +712,13 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void)
|
||||
|
||||
void HAssPublishStatus(void)
|
||||
{
|
||||
Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\","
|
||||
"\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
|
||||
"\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\","
|
||||
"\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,"
|
||||
"\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\","
|
||||
"\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"),
|
||||
my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(),
|
||||
GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(),
|
||||
Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(),
|
||||
WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg);
|
||||
Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_CMND_MODULE " or " D_CMND_TEMPLATE"\":\"%s\","
|
||||
"\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\","
|
||||
"\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_RSSI "\":\"%d\",\"" D_JSON_SIGNAL " (dBm)""\":\"%d\","
|
||||
"\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"LoadAvg\":%lu}"),
|
||||
my_version, my_image, GetBuildDateAndTime().c_str(), ModuleName().c_str(), GetResetReason().c_str(),
|
||||
GetUptime().c_str(), my_hostname, WiFi.localIP().toString().c_str(), WifiGetRssiAsQuality(WiFi.RSSI()),
|
||||
WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), loop_load_avg);
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE));
|
||||
}
|
||||
|
||||
@ -687,8 +737,6 @@ void HAssDiscovery(void)
|
||||
|
||||
if (Settings.flag.hass_discovery || (1 == hass_mode))
|
||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||
// Send info about relays and lights
|
||||
HAssAnnounceRelayLight();
|
||||
|
||||
// Send info about buttons
|
||||
HAssAnnounceButtons();
|
||||
@ -699,6 +747,9 @@ void HAssDiscovery(void)
|
||||
// Send info about sensors
|
||||
HAssAnnounceSensors();
|
||||
|
||||
// Send info about relays and lights
|
||||
HAssAnnounceRelayLight();
|
||||
|
||||
// Send info about status sensor
|
||||
HAssAnnounceDeviceInfoAndStatusSensor();
|
||||
}
|
||||
@ -740,7 +791,9 @@ void HAssAnyKey(void)
|
||||
|
||||
char stopic[TOPSZ];
|
||||
|
||||
if (state == 3) {
|
||||
if (state == 2) {
|
||||
snprintf_P(trg_state, sizeof(trg_state), PSTR("SINGLE"));
|
||||
} else if (state == 3) {
|
||||
snprintf_P(trg_state, sizeof(trg_state), GetStateText(3));
|
||||
} else {
|
||||
GetTextIndexed(trg_state, sizeof(trg_state), state -9, kHAssTriggerStringButtons);
|
||||
@ -796,4 +849,4 @@ bool Xdrv12(uint8_t function)
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_HOME_ASSISTANT
|
||||
#endif // USE_HOME_ASSISTANT
|
||||
|
@ -39,14 +39,14 @@ enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD,
|
||||
const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
|
||||
D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|"
|
||||
D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|"
|
||||
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
|
||||
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
|
||||
D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|"
|
||||
D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPPOSITION;
|
||||
|
||||
void (* const ShutterCommand[])(void) PROGMEM = {
|
||||
&CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterStop, &CmndShutterPosition,
|
||||
&CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay,
|
||||
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
|
||||
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
|
||||
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons,
|
||||
&CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopPosition};
|
||||
|
||||
@ -255,18 +255,24 @@ void ShutterInit(void)
|
||||
}
|
||||
}
|
||||
|
||||
void ShutterReportPosition(bool always)
|
||||
void ShutterReportPosition(bool always, uint32_t index)
|
||||
{
|
||||
Response_P(PSTR("{"));
|
||||
rules_flag.shutter_moving = 0;
|
||||
for (uint32_t i = 0; i < shutters_present; i++) {
|
||||
uint32_t i = 0;
|
||||
uint32_t n = shutters_present;
|
||||
if( index != MAX_SHUTTERS) {
|
||||
i = index;
|
||||
n = index+1;
|
||||
}
|
||||
for (i; i < n; i++) {
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]);
|
||||
uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i);
|
||||
if (Shutter.direction[i] != 0) {
|
||||
rules_flag.shutter_moving = 1;
|
||||
ShutterLogPos(i);
|
||||
}
|
||||
if (i) { ResponseAppend_P(PSTR(",")); }
|
||||
if (i && index == MAX_SHUTTERS) { ResponseAppend_P(PSTR(",")); }
|
||||
uint32_t target = ShutterRealToPercentPosition(Shutter.target_position[i], i);
|
||||
ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i],(Settings.shutter_options[i] & 1) ? 100-target : target );
|
||||
}
|
||||
@ -296,16 +302,16 @@ void ShutterUpdatePosition(void)
|
||||
for (uint32_t i = 0; i < shutters_present; i++) {
|
||||
if (Shutter.direction[i] != 0) {
|
||||
int32_t stop_position_delta = 20;
|
||||
if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) {
|
||||
// Calculate position with counter. Much more accurate and no need for motordelay workaround
|
||||
// adding some steps to stop early
|
||||
Shutter.real_position[i] = ShutterCounterBasedPosition(i);
|
||||
if (!Shutter.start_reported) {
|
||||
ShutterReportPosition(true);
|
||||
XdrvRulesProcess();
|
||||
Shutter.start_reported = 1;
|
||||
}
|
||||
// Calculate position with counter. Much more accurate and no need for motordelay workaround
|
||||
// adding some steps to stop early
|
||||
Shutter.real_position[i] = ShutterCounterBasedPosition(i);
|
||||
if (!Shutter.start_reported) {
|
||||
ShutterReportPosition(true, i);
|
||||
XdrvRulesProcess();
|
||||
Shutter.start_reported = 1;
|
||||
}
|
||||
|
||||
if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) {
|
||||
int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i];
|
||||
int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
|
||||
int32_t min_runtime_ms = Shutter.pwm_frequency[i]*1000 / max_freq_change_per_sec;
|
||||
@ -395,7 +401,7 @@ void ShutterUpdatePosition(void)
|
||||
MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN
|
||||
|
||||
Shutter.direction[i] = 0;
|
||||
ShutterReportPosition(true);
|
||||
ShutterReportPosition(true, i);
|
||||
rules_flag.shutter_moved = 1;
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
@ -910,13 +916,13 @@ void CmndShutterPosition(void)
|
||||
}
|
||||
} else {
|
||||
target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index);
|
||||
ShutterReportPosition(true);
|
||||
ShutterReportPosition(true, index);
|
||||
}
|
||||
XdrvMailbox.index = index +1; // Fix random index for ShutterClose
|
||||
if (XdrvMailbox.command)
|
||||
ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent);
|
||||
} else {
|
||||
ShutterReportPosition(true);
|
||||
ShutterReportPosition(true, MAX_SHUTTERS);
|
||||
if (XdrvMailbox.command)
|
||||
ResponseCmndIdxChar("Locked");
|
||||
}
|
||||
@ -1164,6 +1170,16 @@ void CmndShutterSetClose(void)
|
||||
}
|
||||
}
|
||||
|
||||
void CmndShutterSetOpen(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
|
||||
Shutter.real_position[XdrvMailbox.index -1] = Shutter.open_max[XdrvMailbox.index -1];
|
||||
ShutterStartInit(XdrvMailbox.index -1, 0, Shutter.open_max[XdrvMailbox.index -1]);
|
||||
Settings.shutter_position[XdrvMailbox.index -1] = 100;
|
||||
ResponseCmndIdxChar(D_CONFIGURATION_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
void CmndShutterInvert(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
|
||||
@ -1261,7 +1277,7 @@ bool Xdrv27(uint8_t function)
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
//case FUNC_EVERY_250_MSECOND:
|
||||
ShutterReportPosition(false);
|
||||
ShutterReportPosition(false, MAX_SHUTTERS);
|
||||
break;
|
||||
|
||||
case FUNC_COMMAND:
|
||||
|
1648
tasmota/xdrv_39_thermostat.ino
Normal file
1648
tasmota/xdrv_39_thermostat.ino
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
xdrv_39_webcam.ino - ESP32 webcam support for Tasmota
|
||||
xdrv_81_webcam.ino - ESP32 webcam support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Gerhard Mutz and Theo Arends
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
* board_build.f_cpu = 240000000L
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_39 39
|
||||
#define XDRV_81 81
|
||||
|
||||
#define CAMERA_MODEL_AI_THINKER
|
||||
|
||||
@ -867,7 +867,7 @@ void CmndWebcam(void) {
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv39(uint8_t function) {
|
||||
bool Xdrv81(uint8_t function) {
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
@ -878,13 +878,13 @@ bool Xdrv39(uint8_t function) {
|
||||
wc_pic_setup();
|
||||
break;
|
||||
case FUNC_WEB_ADD_MAIN_BUTTON:
|
||||
//if (Settings.esp32_webcam_resolution) {
|
||||
#ifndef USE_SCRIPT
|
||||
if (Settings.esp32_webcam_resolution) {
|
||||
//#ifndef USE_SCRIPT
|
||||
WcStreamControl(Settings.esp32_webcam_resolution);
|
||||
delay(50); // Give the webcam webserver some time to prepare the stream
|
||||
wc_show_stream();
|
||||
#endif
|
||||
//}
|
||||
//#endif
|
||||
}
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kWCCommands, WCCommand);
|
@ -48,6 +48,7 @@
|
||||
// #undef USE_TSL2561 // possible address conflict on the I2C-bus
|
||||
// #endif
|
||||
// #endif
|
||||
|
||||
#define XSNS_27 27
|
||||
#define XI2C_21 21 // See I2CDEVICES.md
|
||||
|
||||
@ -61,17 +62,9 @@
|
||||
#define APDS9930_CHIPID_1 0x12 // we will check, if someone got an incorrect sensor
|
||||
#define APDS9930_CHIPID_2 0x39 // there are case reports about "accidentially bought" 9930's
|
||||
|
||||
|
||||
// TODO() : Move to my_user_config.h file
|
||||
#define USE_APDS9960_GESTURE // Enable Gesture feature (+2k code)
|
||||
#define USE_APDS9960_PROXIMITY // Enable Proximity feature (>50 code)
|
||||
#define USE_APDS9960_COLOR // Enable Color feature (+0.8k code)
|
||||
|
||||
#define APDS9960_MODE_GESTURE 0
|
||||
#define APDS9960_MODE_COLOR 1
|
||||
|
||||
#define USE_APDS9960_STARTMODE APDS9960_MODE_GESTURE
|
||||
|
||||
/* Gesture parameters */
|
||||
#define GESTURE_THRESHOLD_OUT 10
|
||||
#define GESTURE_SENSITIVITY_1 50
|
||||
@ -80,19 +73,8 @@
|
||||
#define APDS9960_LONG_RECOVERY 50 // long pause after sensor overload in loops
|
||||
#define APDS9960_MAX_GESTURE_CYCLES 50 // how many FIFO-reads are allowed to prevent crash
|
||||
|
||||
|
||||
// TODO() : Move to Translate file
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
#define D_UNIT_KELVIN "°K"
|
||||
|
||||
/******************************************************************************\
|
||||
* constants
|
||||
* Constants
|
||||
\******************************************************************************/
|
||||
|
||||
const char APDS9960_TAG[] PROGMEM = "APDS9960"; // Only one actualy
|
||||
|
375
tasmota/xsns_68_windmeter.ino
Normal file
375
tasmota/xsns_68_windmeter.ino
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
xsns_68_windmeter.ino - Analog wind sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Matteo Albinola
|
||||
(inspired by great works of Thomas Eckerstorfer, Norbert Richter, Maarten Damen and Theo Arends)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_WINDMETER
|
||||
/*********************************************************************************************\
|
||||
* WindMeter sensor (speed)
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_68 68
|
||||
|
||||
#define D_WINDMETER_NAME "WindMeter"
|
||||
|
||||
#define WINDMETER_DEF_RADIUS 61 // Radius in millimeters (calculated by measuring the distance from the centre to the edge of one of the cups)
|
||||
#define WINDMETER_DEF_PULSES_X_ROT 1 // Number of pulses for a complete rotation
|
||||
#define WINDMETER_DEF_PULSE_DEBOUNCE 10 // Pulse counter debounce time (milliseconds)
|
||||
#define WINDMETER_DEF_COMP_FACTOR 1.18 // Compensation factor
|
||||
#define WINDMETER_DEF_TELE_PCHANGE 255 // Minimum percentage change between current and last reported speed in order to trigger a new tele message (0...100, 255 means off)
|
||||
#define WINDMETER_WEIGHT_AVG_SAMPLE 150 // No of samples to take
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
#define D_WINDMETER_WIND_AVG "∅"
|
||||
#define D_WINDMETER_WIND_ANGLE "∠"
|
||||
#define D_WINDMETER_WIND_DEGREE "°"
|
||||
const char HTTP_SNS_WINDMETER[] PROGMEM =
|
||||
"{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}"
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
"{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED " " D_WINDMETER_WIND_AVG "{m}%s %s{e}"
|
||||
"{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}"
|
||||
"{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}"
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
// "{s}WindMeter " D_TX20_WIND_DIRECTION "{m}%s %s" D_WINDMETER_WIND_DEGREE "{e}"
|
||||
//#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
// "{s}WindMeter " D_TX20_WIND_DIRECTION " " D_WINDMETER_WIND_AVG "{m}%s %s" D_WINDMETER_WIND_DEGREE "{e}"
|
||||
// "{s}WindMeter " D_TX20_WIND_DIRECTION " " D_WINDMETER_WIND_ANGLE "{m}%s" D_WINDMETER_WIND_DEGREE " (%s,%s)" D_WINDMETER_WIND_DEGREE;
|
||||
//#endif // USE_WINDMETER_NOSTATISTICS
|
||||
;
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
// float saves 48 byte
|
||||
float const windmeter_pi = 3.1415926535897932384626433; // Pi
|
||||
float const windmeter_2pi = windmeter_pi * 2;
|
||||
|
||||
struct WINDMETER {
|
||||
uint32_t counter_time;
|
||||
unsigned long counter = 0;
|
||||
//uint32_t speed_time;
|
||||
float speed = 0;
|
||||
float last_tele_speed = 0;
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
float speed_min = 0;
|
||||
float speed_max = 0;
|
||||
float speed_avg = 0;
|
||||
uint32_t samples_count = 0;
|
||||
uint32_t avg_samples_no;
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
} WindMeter;
|
||||
|
||||
#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception
|
||||
void WindMeterUpdateSpeed(void) ICACHE_RAM_ATTR;
|
||||
#endif // ARDUINO_ESP8266_RELEASE_2_3_0
|
||||
|
||||
void WindMeterUpdateSpeed(void)
|
||||
{
|
||||
uint32_t time = micros();
|
||||
uint32_t time_diff = time - WindMeter.counter_time;
|
||||
if (time_diff > Settings.windmeter_pulse_debounce * 1000) {
|
||||
WindMeter.counter_time = time;
|
||||
WindMeter.counter++;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void WindMeterInit(void)
|
||||
{
|
||||
if (!Settings.flag2.speed_conversion) {
|
||||
Settings.flag2.speed_conversion = 2; // 0 = none, 1 = m/s, 2 = km/h, 3 = kn, 4 = mph, 5 = ft/s, 6 = yd/s
|
||||
}
|
||||
if (!Settings.windmeter_radius) {
|
||||
Settings.windmeter_radius = WINDMETER_DEF_RADIUS;
|
||||
}
|
||||
if (!Settings.windmeter_pulses_x_rot) {
|
||||
Settings.windmeter_pulses_x_rot = WINDMETER_DEF_PULSES_X_ROT;
|
||||
}
|
||||
if (!Settings.windmeter_pulse_debounce) {
|
||||
Settings.windmeter_pulse_debounce = WINDMETER_DEF_PULSE_DEBOUNCE;
|
||||
}
|
||||
if (!Settings.windmeter_speed_factor) {
|
||||
Settings.windmeter_speed_factor = (int16_t)(WINDMETER_DEF_COMP_FACTOR * 1000);
|
||||
}
|
||||
if (!Settings.windmeter_tele_pchange) {
|
||||
Settings.windmeter_tele_pchange = WINDMETER_DEF_TELE_PCHANGE;
|
||||
}
|
||||
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
WindMeterResetStatData();
|
||||
WindMeterCheckSampleCount();
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
|
||||
pinMode(Pin(GPIO_WINDMETER_SPEED), INPUT_PULLUP);
|
||||
attachInterrupt(Pin(GPIO_WINDMETER_SPEED), WindMeterUpdateSpeed, FALLING);
|
||||
}
|
||||
|
||||
void WindMeterEverySecond(void)
|
||||
{
|
||||
//uint32_t time = micros();
|
||||
//uint32_t delta_time = time - WindMeter.speed_time;
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("delta_time: %d"), delta_time);
|
||||
|
||||
// speed = ( (pulses / pulses_per_rotation) * (2 * pi * radius) ) / delta_time
|
||||
WindMeter.speed = ((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) * ((float)Settings.windmeter_speed_factor / 1000);
|
||||
//WindMeter.speed = (((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) / ((float)delta_time / 1000000)) * ((float)Settings.windmeter_speed_factor / 1000);
|
||||
WindMeter.counter = 0;
|
||||
//WindMeter.speed_time = time;
|
||||
|
||||
//char speed_string[FLOATSZ];
|
||||
//dtostrfd(WindMeter.speed, 2, speed_string);
|
||||
//char uspeed_string[FLOATSZ];
|
||||
//dtostrfd(ConvertSpeed(WindMeter.speed), 2, uspeed_string);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Speed %s [m/s] - %s [unit]"), speed_string, uspeed_string);
|
||||
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
if (WindMeter.speed < WindMeter.speed_min) {
|
||||
WindMeter.speed_min = WindMeter.speed;
|
||||
}
|
||||
if (WindMeter.speed > WindMeter.speed_max) {
|
||||
WindMeter.speed_max = WindMeter.speed;
|
||||
}
|
||||
|
||||
// exponentially weighted average is not quite as smooth as the arithmetic average
|
||||
// but close enough to the moving average and does not require the regular reset
|
||||
// of the divider with the associated jump in avg values after period is over
|
||||
if (WindMeter.samples_count <= WindMeter.avg_samples_no) {
|
||||
WindMeter.samples_count++;
|
||||
}
|
||||
WindMeter.speed_avg -= WindMeter.speed_avg / WindMeter.samples_count;
|
||||
WindMeter.speed_avg += float(WindMeter.speed) / WindMeter.samples_count;
|
||||
|
||||
WindMeterCheckSampleCount();
|
||||
if (0==Settings.tele_period) {
|
||||
WindMeterResetStatData();
|
||||
}
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
|
||||
if (WindMeterShouldTriggerTele()) {
|
||||
WindMeterTriggerTele();
|
||||
}
|
||||
}
|
||||
|
||||
bool WindMeterShouldTriggerTele()
|
||||
{
|
||||
if (Settings.windmeter_tele_pchange > 100) {
|
||||
return false;
|
||||
} else if (WindMeter.last_tele_speed == 0) {
|
||||
return WindMeter.speed > 0;
|
||||
} else {
|
||||
float perc_change = (WindMeter.speed / WindMeter.last_tele_speed) -1;
|
||||
return (perc_change * ((perc_change < 0) ? -100 : 100)) >= Settings.windmeter_tele_pchange;
|
||||
}
|
||||
}
|
||||
|
||||
void WindMeterResetStatData(void)
|
||||
{
|
||||
WindMeter.speed_min = WindMeter.speed;
|
||||
WindMeter.speed_max = WindMeter.speed;
|
||||
//WindMeter.direction_min = WindMeter.direction;
|
||||
//WindMeter.direction_max = WindMeter.direction;
|
||||
}
|
||||
|
||||
void WindMeterCheckSampleCount(void)
|
||||
{
|
||||
uint32_t prev_avg_samples_no = WindMeter.avg_samples_no;
|
||||
if (Settings.tele_period) {
|
||||
// number for avg samples = teleperiod value if set
|
||||
WindMeter.avg_samples_no = Settings.tele_period;
|
||||
} else {
|
||||
// otherwise use default number of samples for this driver
|
||||
WindMeter.avg_samples_no = WINDMETER_WEIGHT_AVG_SAMPLE;
|
||||
}
|
||||
if (prev_avg_samples_no != WindMeter.avg_samples_no) {
|
||||
WindMeter.speed_avg = WindMeter.speed;
|
||||
WindMeter.samples_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WindMeterShow(bool json)
|
||||
{
|
||||
char speed_string[FLOATSZ];
|
||||
dtostrfd(ConvertSpeed(WindMeter.speed), 2, speed_string);
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
char speed_min_string[FLOATSZ];
|
||||
dtostrfd(ConvertSpeed(WindMeter.speed_min), 2, speed_min_string);
|
||||
char speed_max_string[FLOATSZ];
|
||||
dtostrfd(ConvertSpeed(WindMeter.speed_max), 2, speed_max_string);
|
||||
char speed_avg_string[FLOATSZ];
|
||||
dtostrfd(ConvertSpeed(WindMeter.speed_avg), 2, speed_avg_string);
|
||||
//char direction_avg_string[FLOATSZ];
|
||||
//dtostrfd(WindMeter.direction_avg, 1, direction_avg_string);
|
||||
//char direction_avg_cardinal_string[4];
|
||||
//GetTextIndexed(direction_avg_cardinal_string, sizeof(direction_avg_cardinal_string), int((WindMeter.direction_avg/22.5f)+0.5f) % 16, kWindMeterDirections);
|
||||
//char direction_range_string[FLOATSZ];
|
||||
//dtostrfd(Tx2xNormalize(WindMeter.direction_max-WindMeter.direction_min)*22.5, 1, direction_range_string);
|
||||
//char direction_min_string[FLOATSZ];
|
||||
//dtostrfd(Tx2xNormalize(WindMeter.direction_min)*22.5, 1, direction_min_string);
|
||||
//char direction_max_string[FLOATSZ];
|
||||
//dtostrfd(Tx2xNormalize(WindMeter.direction_max)*22.5, 1, direction_max_string);
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
|
||||
if (json) {
|
||||
WindMeter.last_tele_speed = WindMeter.speed;
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"),
|
||||
speed_string,
|
||||
speed_avg_string,
|
||||
speed_min_string,
|
||||
speed_max_string,
|
||||
"n/a", //direction_cardinal_string,
|
||||
"n/a", //direction_string,
|
||||
"n/a", //direction_avg_string,
|
||||
"n/a", //direction_avg_cardinal_string,
|
||||
"n/a", //direction_min_string,
|
||||
"n/a", //direction_max_string,
|
||||
"n/a" //direction_range_string
|
||||
);
|
||||
#else // USE_WINDMETER_NOSTATISTICS
|
||||
ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"),
|
||||
speed_string,
|
||||
"n/a", //wind_direction_cardinal_string,
|
||||
"n/a" //wind_direction_string
|
||||
);
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_WINDMETER,
|
||||
speed_string,
|
||||
SpeedUnit().c_str(),
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
speed_avg_string,
|
||||
SpeedUnit().c_str(),
|
||||
speed_min_string,
|
||||
SpeedUnit().c_str(),
|
||||
speed_max_string,
|
||||
SpeedUnit().c_str(),
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
"n/a", //wind_direction_cardinal_string,
|
||||
"n/a" //wind_direction_string
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
,"n/a", //,wind_direction_avg_cardinal_string,
|
||||
"n/a", //wind_direction_avg_string,
|
||||
"n/a", //wind_direction_range_string,
|
||||
"n/a", //wind_direction_min_string,
|
||||
"n/a" //wind_direction_max_string
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
void WindMeterTriggerTele(void)
|
||||
{
|
||||
mqtt_data[0] = '\0';
|
||||
if (MqttShowSensor()) {
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
|
||||
#ifdef USE_RULES
|
||||
RulesTeleperiod(); // Allow rule based HA messages
|
||||
#endif // USE_RULES
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns68Cmnd(void)
|
||||
{
|
||||
bool serviced = true;
|
||||
bool show_parms = true;
|
||||
char sub_string[XdrvMailbox.data_len +1];
|
||||
switch (XdrvMailbox.payload) {
|
||||
case 1:
|
||||
if (strstr(XdrvMailbox.data, ",") != nullptr) {
|
||||
Settings.windmeter_radius = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strstr(XdrvMailbox.data, ",") != nullptr) {
|
||||
Settings.windmeter_pulses_x_rot = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (strstr(XdrvMailbox.data, ",") != nullptr) {
|
||||
Settings.windmeter_pulse_debounce = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (strstr(XdrvMailbox.data, ",") != nullptr) {
|
||||
Settings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (strstr(XdrvMailbox.data, ",") != nullptr) {
|
||||
Settings.windmeter_tele_pchange = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (show_parms) {
|
||||
char speed_factor_string[FLOATSZ];
|
||||
dtostrfd((float)Settings.windmeter_speed_factor / 1000, 3, speed_factor_string);
|
||||
char tele_pchange_string[4] = "off";
|
||||
if (Settings.windmeter_tele_pchange <= 100) {
|
||||
itoa(Settings.windmeter_tele_pchange, tele_pchange_string, 10);
|
||||
}
|
||||
Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s,\"TeleTriggerMin%Change\":%s}}"),
|
||||
Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string, tele_pchange_string);
|
||||
}
|
||||
return serviced;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns68(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
if (PinUsed(GPIO_WINDMETER_SPEED)) {
|
||||
switch (function) {
|
||||
case FUNC_INIT:
|
||||
WindMeterInit();
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
WindMeterEverySecond();
|
||||
break;
|
||||
#ifndef USE_WINDMETER_NOSTATISTICS
|
||||
case FUNC_AFTER_TELEPERIOD:
|
||||
WindMeterResetStatData();
|
||||
break;
|
||||
#endif // USE_WINDMETER_NOSTATISTICS
|
||||
case FUNC_JSON_APPEND:
|
||||
WindMeterShow(true);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
WindMeterShow(false);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
case FUNC_COMMAND_SENSOR:
|
||||
if (XSNS_68 == XdrvMailbox.index) {
|
||||
result = Xsns68Cmnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_WINDMETER
|
598
tasmota/xsns_69_opentherm.ino
Normal file
598
tasmota/xsns_69_opentherm.ino
Normal file
@ -0,0 +1,598 @@
|
||||
/*
|
||||
xsns_69_opentherm.ino - OpenTherm protocol support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Yuriy Sannikov
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_OPENTHERM
|
||||
|
||||
#define XSNS_69 69
|
||||
|
||||
#include <OpenTherm.h>
|
||||
|
||||
// Hot water and boiler parameter ranges
|
||||
#define OT_HOT_WATER_MIN 23
|
||||
#define OT_HOT_WATER_MAX 55
|
||||
#define OT_BOILER_MIN 40
|
||||
#define OT_BOILER_MAX 85
|
||||
|
||||
#define OT_HOT_WATER_DEFAULT 36;
|
||||
#define OT_BOILER_DEFAULT 85;
|
||||
|
||||
// Seconds before OT will make an attempt to connect to the boiler after connection error
|
||||
#define SNS_OT_DISCONNECT_COOLDOWN_SECONDS 10
|
||||
|
||||
// Count of the OpenThermSettingsFlags
|
||||
#define OT_FLAGS_COUNT 6
|
||||
enum OpenThermSettingsFlags
|
||||
{
|
||||
// If set, central heating on/off state follows diagnostic indication bit(6), however
|
||||
// EnableCentralHeating flag has a priority over it
|
||||
EnableCentralHeatingOnDiagnostics = 0x01,
|
||||
// If set, DHW is on after restart.
|
||||
EnableHotWater = 0x02,
|
||||
// If set, keep CH always on after restart. If off, follows the EnableCentralHeatingOnDiagnostics rule
|
||||
EnableCentralHeating = 0x04,
|
||||
EnableCooling = 0x08,
|
||||
EnableTemperatureCompensation = 0x10,
|
||||
EnableCentralHeating2 = 0x20,
|
||||
};
|
||||
|
||||
enum OpenThermConnectionStatus
|
||||
{
|
||||
OTC_NONE, // OT not initialized
|
||||
OTC_DISCONNECTED, // OT communication timed out
|
||||
OTC_CONNECTING, // Connecting after start or from DISCONNECTED state
|
||||
OTC_HANDSHAKE, // Wait for the handshake response
|
||||
OTC_READY, // Last Known Good response state is SUCCESS and no requests are in flight
|
||||
OTC_INFLIGHT // Request sent, waiting from the response
|
||||
};
|
||||
|
||||
OpenThermConnectionStatus sns_ot_connection_status = OpenThermConnectionStatus::OTC_NONE;
|
||||
uint8_t sns_ot_disconnect_cooldown = 0;
|
||||
|
||||
OpenTherm *sns_ot_master = NULL;
|
||||
|
||||
// Has valid values if connection status is READY or INFLIGHT
|
||||
typedef struct OT_BOILER_STATUS_T
|
||||
{
|
||||
// Boiler fault code
|
||||
uint8_t m_fault_code;
|
||||
// Boiler OEM fault code
|
||||
uint8_t m_oem_fault_code;
|
||||
// Boilder OEM Diagnostics code
|
||||
uint16_t m_oem_diag_code;
|
||||
// OpenTherm ID(3) response.
|
||||
uint8_t m_slave_flags;
|
||||
// OpenTherm ID(1) codes. Should be used to display state
|
||||
unsigned long m_slave_raw_status;
|
||||
// Desired boiler states
|
||||
bool m_enableCentralHeating;
|
||||
bool m_enableHotWater;
|
||||
bool m_enableCooling;
|
||||
bool m_enableOutsideTemperatureCompensation;
|
||||
bool m_enableCentralHeating2;
|
||||
|
||||
// Some boilers has an input for the heat request. When short, heat is requested
|
||||
// OT ID(0) bit 6 may indicate state of the Heat Request input
|
||||
// By enabling this bit we will set m_enableCentralHeating to true when OT ID(0) bit 6 is set.
|
||||
// This enables to use external mechanical thermostat to enable heating.
|
||||
// Some of the use cases might be setting an emergency temperature to prevent freezing
|
||||
// in case of the software thermostat failure.
|
||||
bool m_useDiagnosticIndicationAsHeatRequest;
|
||||
|
||||
// Hot Water temperature
|
||||
float m_hotWaterSetpoint_read;
|
||||
// Flame Modulation
|
||||
float m_flame_modulation_read;
|
||||
// Boiler Temperature
|
||||
float m_boiler_temperature_read;
|
||||
|
||||
// Boiler desired values
|
||||
float m_boilerSetpoint;
|
||||
float m_hotWaterSetpoint;
|
||||
|
||||
} OT_BOILER_STATUS;
|
||||
|
||||
OT_BOILER_STATUS sns_ot_boiler_status;
|
||||
|
||||
const char *sns_opentherm_connection_stat_to_str(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case OpenThermConnectionStatus::OTC_NONE:
|
||||
return "NONE";
|
||||
case OpenThermConnectionStatus::OTC_DISCONNECTED:
|
||||
return "FAULT";
|
||||
case OpenThermConnectionStatus::OTC_CONNECTING:
|
||||
return "CONNECTING";
|
||||
case OpenThermConnectionStatus::OTC_HANDSHAKE:
|
||||
return "HANDSHAKE";
|
||||
case OpenThermConnectionStatus::OTC_READY:
|
||||
return "READY";
|
||||
case OpenThermConnectionStatus::OTC_INFLIGHT:
|
||||
return "BUSY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void sns_opentherm_init_boiler_status()
|
||||
{
|
||||
memset(&sns_ot_boiler_status, 0, sizeof(OT_BOILER_STATUS));
|
||||
|
||||
// Settings
|
||||
sns_ot_boiler_status.m_useDiagnosticIndicationAsHeatRequest = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics;
|
||||
sns_ot_boiler_status.m_enableHotWater = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableHotWater;
|
||||
sns_ot_boiler_status.m_enableCentralHeating = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating;
|
||||
sns_ot_boiler_status.m_enableCooling = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCooling;
|
||||
sns_ot_boiler_status.m_enableOutsideTemperatureCompensation = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableTemperatureCompensation;
|
||||
sns_ot_boiler_status.m_enableCentralHeating2 = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating2;
|
||||
|
||||
sns_ot_boiler_status.m_boilerSetpoint = (float)Settings.ot_boiler_setpoint;
|
||||
sns_ot_boiler_status.m_hotWaterSetpoint = (float)Settings.ot_hot_water_setpoint;
|
||||
|
||||
sns_ot_boiler_status.m_fault_code = 0;
|
||||
sns_ot_boiler_status.m_oem_fault_code = 0;
|
||||
sns_ot_boiler_status.m_oem_diag_code = 0;
|
||||
sns_ot_boiler_status.m_hotWaterSetpoint_read = 0;
|
||||
sns_ot_boiler_status.m_flame_modulation_read = 0;
|
||||
sns_ot_boiler_status.m_boiler_temperature_read = 0;
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR sns_opentherm_handleInterrupt()
|
||||
{
|
||||
sns_ot_master->handleInterrupt();
|
||||
}
|
||||
|
||||
void sns_opentherm_processResponseCallback(unsigned long response, int st)
|
||||
{
|
||||
OpenThermResponseStatus status = (OpenThermResponseStatus)st;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE,
|
||||
PSTR("[OTH]: Processing response. Status=%s, Response=0x%lX"),
|
||||
sns_ot_master->statusToString(status), response);
|
||||
|
||||
if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_HANDSHAKE)
|
||||
{
|
||||
return sns_ot_process_handshake(response, st);
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case OpenThermResponseStatus::SUCCESS:
|
||||
if (sns_ot_master->isValidResponse(response))
|
||||
{
|
||||
sns_opentherm_process_success_response(&sns_ot_boiler_status, response);
|
||||
}
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY;
|
||||
break;
|
||||
|
||||
case OpenThermResponseStatus::INVALID:
|
||||
sns_opentherm_check_retry_request();
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY;
|
||||
break;
|
||||
|
||||
// Timeout may indicate not valid/supported command or connection error
|
||||
// In this case we do reconnect.
|
||||
// If this command will timeout multiple times, it will be excluded from the rotation later on
|
||||
// after couple of failed attempts. See sns_opentherm_check_retry_request logic
|
||||
case OpenThermResponseStatus::TIMEOUT:
|
||||
sns_opentherm_check_retry_request();
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool sns_opentherm_Init()
|
||||
{
|
||||
if (PinUsed(GPIO_BOILER_OT_RX) && PinUsed(GPIO_BOILER_OT_TX))
|
||||
{
|
||||
sns_ot_master = new OpenTherm(Pin(GPIO_BOILER_OT_RX), Pin(GPIO_BOILER_OT_TX));
|
||||
sns_ot_master->begin(sns_opentherm_handleInterrupt, sns_opentherm_processResponseCallback);
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING;
|
||||
|
||||
sns_opentherm_init_boiler_status();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// !warning, sns_opentherm settings are not ready at this point
|
||||
}
|
||||
|
||||
void sns_opentherm_stat(bool json)
|
||||
{
|
||||
if (!sns_ot_master)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const char *statusStr = sns_opentherm_connection_stat_to_str(sns_ot_connection_status);
|
||||
|
||||
if (json)
|
||||
{
|
||||
ResponseAppend_P(PSTR(",\"OPENTHERM\":{"));
|
||||
ResponseAppend_P(PSTR("\"conn\":\"%s\","), statusStr);
|
||||
ResponseAppend_P(PSTR("\"settings\":%d,"), Settings.ot_flags);
|
||||
sns_opentherm_dump_telemetry();
|
||||
ResponseJsonEnd();
|
||||
#ifdef USE_WEBSERVER
|
||||
}
|
||||
else
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}OpenTherm status{m}%s (0x%X){e}"), statusStr, (int)sns_ot_boiler_status.m_slave_flags);
|
||||
if (sns_ot_connection_status < OpenThermConnectionStatus::OTC_READY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
WSContentSend_P(PSTR("{s}Std/OEM Fault Codes{m}%d / %d{e}"),
|
||||
(int)sns_ot_boiler_status.m_fault_code,
|
||||
(int)sns_ot_boiler_status.m_oem_fault_code);
|
||||
|
||||
WSContentSend_P(PSTR("{s}OEM Diagnostic Code{m}%d{e}"),
|
||||
(int)sns_ot_boiler_status.m_oem_diag_code);
|
||||
|
||||
WSContentSend_P(PSTR("{s}Hot Water Setpoint{m}%d{e}"),
|
||||
(int)sns_ot_boiler_status.m_hotWaterSetpoint_read);
|
||||
|
||||
WSContentSend_P(PSTR("{s}Flame Modulation{m}%d{e}"),
|
||||
(int)sns_ot_boiler_status.m_flame_modulation_read);
|
||||
|
||||
WSContentSend_P(PSTR("{s}Boiler Temp/Setpnt{m}%d / %d{e}"),
|
||||
(int)sns_ot_boiler_status.m_boiler_temperature_read,
|
||||
(int)sns_ot_boiler_status.m_boilerSetpoint);
|
||||
|
||||
if (OpenTherm::isCentralHeatingActive(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Central Heating is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (sns_ot_boiler_status.m_enableHotWater)
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Hot Water is Enabled{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isHotWaterActive(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Hot Water is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isFlameOn(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Flame is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (sns_ot_boiler_status.m_enableCooling)
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Cooling is Enabled{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isCoolingActive(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Cooling is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isDiagnostic(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Diagnostic Indication{m}{e}"));
|
||||
}
|
||||
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
void sns_ot_start_handshake()
|
||||
{
|
||||
if (!sns_ot_master)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: perform handshake"));
|
||||
|
||||
sns_ot_master->sendRequestAync(
|
||||
OpenTherm::buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0));
|
||||
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_HANDSHAKE;
|
||||
}
|
||||
|
||||
void sns_ot_process_handshake(unsigned long response, int st)
|
||||
{
|
||||
OpenThermResponseStatus status = (OpenThermResponseStatus)st;
|
||||
|
||||
if (status != OpenThermResponseStatus::SUCCESS || !sns_ot_master->isValidResponse(response))
|
||||
{
|
||||
AddLog_P2(LOG_LEVEL_ERROR,
|
||||
PSTR("[OTH]: getSlaveConfiguration failed. Status=%s"),
|
||||
sns_ot_master->statusToString(status));
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: getLastResponseStatus SUCCESS. Slave Cfg: %lX"), response);
|
||||
|
||||
sns_ot_boiler_status.m_slave_flags = (response & 0xFF00) >> 8;
|
||||
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY;
|
||||
}
|
||||
|
||||
void sns_opentherm_CheckSettings(void)
|
||||
{
|
||||
bool settingsValid = true;
|
||||
|
||||
settingsValid &= Settings.ot_hot_water_setpoint >= OT_HOT_WATER_MIN;
|
||||
settingsValid &= Settings.ot_hot_water_setpoint <= OT_HOT_WATER_MAX;
|
||||
settingsValid &= Settings.ot_boiler_setpoint >= OT_BOILER_MIN;
|
||||
settingsValid &= Settings.ot_boiler_setpoint <= OT_BOILER_MAX;
|
||||
|
||||
if (!settingsValid)
|
||||
{
|
||||
Settings.ot_hot_water_setpoint = OT_HOT_WATER_DEFAULT;
|
||||
Settings.ot_boiler_setpoint = OT_BOILER_DEFAULT;
|
||||
Settings.ot_flags =
|
||||
OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics |
|
||||
OpenThermSettingsFlags::EnableHotWater;
|
||||
}
|
||||
}
|
||||
/*********************************************************************************************\
|
||||
* Command Processing
|
||||
\*********************************************************************************************/
|
||||
const char *sns_opentherm_flag_text(uint8_t mode)
|
||||
{
|
||||
switch ((OpenThermSettingsFlags)mode)
|
||||
{
|
||||
case OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics:
|
||||
return "CHOD";
|
||||
case OpenThermSettingsFlags::EnableHotWater:
|
||||
return "DHW";
|
||||
case OpenThermSettingsFlags::EnableCentralHeating:
|
||||
return "CH";
|
||||
case OpenThermSettingsFlags::EnableCooling:
|
||||
return "COOL";
|
||||
case OpenThermSettingsFlags::EnableTemperatureCompensation:
|
||||
return "OTC";
|
||||
case OpenThermSettingsFlags::EnableCentralHeating2:
|
||||
return "CH2";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t sns_opentherm_parse_flag(char *flag)
|
||||
{
|
||||
if (!strncmp(flag, "CHOD", 4))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics;
|
||||
}
|
||||
else if (!strncmp(flag, "COOL", 4))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCooling;
|
||||
}
|
||||
else if (!strncmp(flag, "DHW", 3))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableHotWater;
|
||||
}
|
||||
else if (!strncmp(flag, "OTC", 3))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableTemperatureCompensation;
|
||||
}
|
||||
else if (!strncmp(flag, "CH2", 3))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCentralHeating2;
|
||||
}
|
||||
else if (!strncmp(flag, "CH", 2))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCentralHeating;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t sns_opentherm_read_flags(char *data, uint32_t len)
|
||||
{
|
||||
uint8_t tokens = 1;
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
if (data[i] == ',')
|
||||
{
|
||||
++tokens;
|
||||
}
|
||||
}
|
||||
uint8_t result = 0;
|
||||
char sub_string[XdrvMailbox.data_len + 1];
|
||||
for (int i = 1; i <= tokens; ++i)
|
||||
{
|
||||
char *flag = subStr(sub_string, data, ",", i);
|
||||
if (!flag)
|
||||
{
|
||||
break;
|
||||
}
|
||||
result |= sns_opentherm_parse_flag(flag);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#define D_PRFX_OTHERM "ot_"
|
||||
// set the boiler temperature (CH). Sutable for the PID app.
|
||||
// After restart will use the defaults from the settings
|
||||
#define D_CMND_OTHERM_BOILER_SETPOINT "tboiler"
|
||||
// set hot water (DHW) temperature. Do not write it in the flash memory.
|
||||
// suitable for the temporary changes
|
||||
#define D_CMND_OTHERM_DHW_SETPOINT "twater"
|
||||
// This command will save CH and DHW setpoints into the settings. Those values will be used after system restart
|
||||
// The reason to separate set and save is to reduce flash memory write count, especially if boiler temperature is controlled
|
||||
// by the PID thermostat
|
||||
#define D_CMND_OTHERM_SAVE_SETTINGS "save_setpoints"
|
||||
// Get or set flags
|
||||
|
||||
// EnableCentralHeatingOnDiagnostics -> CHOD
|
||||
// EnableHotWater -> DHW
|
||||
// EnableCentralHeating -> CH
|
||||
// EnableCooling -> COOL
|
||||
// EnableTemperatureCompensation -> OTC
|
||||
// EnableCentralHeating2 -> CH2
|
||||
#define D_CMND_OTHERM_FLAGS "flags"
|
||||
|
||||
// Get/Set boiler status m_enableCentralHeating value. It's equivalent of the EnableCentralHeating settings
|
||||
// flag value, however, this command does not update the settings.
|
||||
// Usefull to buld automations
|
||||
// Please note, if you set it to "0" and EnableCentralHeatingOnDiagnostics is set
|
||||
// boiler will follow the Diagnostics bit and won't turn CH off. When Diagnostics bit cleared,
|
||||
// and "ot_ch" is "1", boiler will keep heating
|
||||
#define D_CMND_SET_CENTRAL_HEATING_ENABLED "ch"
|
||||
|
||||
const char kOpenThermCommands[] PROGMEM = D_PRFX_OTHERM "|" D_CMND_OTHERM_BOILER_SETPOINT "|" D_CMND_OTHERM_DHW_SETPOINT
|
||||
"|" D_CMND_OTHERM_SAVE_SETTINGS "|" D_CMND_OTHERM_FLAGS "|" D_CMND_SET_CENTRAL_HEATING_ENABLED;
|
||||
|
||||
void (*const OpenThermCommands[])(void) PROGMEM = {
|
||||
&sns_opentherm_boiler_setpoint_cmd,
|
||||
&sns_opentherm_hot_water_setpoint_cmd,
|
||||
&sns_opentherm_save_settings_cmd,
|
||||
&sns_opentherm_flags_cmd,
|
||||
&sns_opentherm_set_central_heating_cmd};
|
||||
|
||||
void sns_opentherm_cmd(void) { }
|
||||
void sns_opentherm_boiler_setpoint_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
sns_ot_boiler_status.m_boilerSetpoint = atof(XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndFloat(sns_ot_boiler_status.m_boilerSetpoint, Settings.flag2.temperature_resolution);
|
||||
}
|
||||
|
||||
void sns_opentherm_hot_water_setpoint_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
sns_ot_boiler_status.m_hotWaterSetpoint = atof(XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndFloat(sns_ot_boiler_status.m_hotWaterSetpoint, Settings.flag2.temperature_resolution);
|
||||
}
|
||||
|
||||
void sns_opentherm_save_settings_cmd(void)
|
||||
{
|
||||
Settings.ot_hot_water_setpoint = (uint8_t)sns_ot_boiler_status.m_hotWaterSetpoint;
|
||||
Settings.ot_boiler_setpoint = (uint8_t)sns_ot_boiler_status.m_boilerSetpoint;
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
void sns_opentherm_flags_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
// Set flags value
|
||||
Settings.ot_flags = sns_opentherm_read_flags(XdrvMailbox.data, XdrvMailbox.data_len);
|
||||
// Reset boiler status to apply settings
|
||||
sns_opentherm_init_boiler_status();
|
||||
}
|
||||
bool addComma = false;
|
||||
mqtt_data[0] = 0;
|
||||
for (int pos = 0; pos < OT_FLAGS_COUNT; ++pos)
|
||||
{
|
||||
int mask = 1 << pos;
|
||||
int mode = Settings.ot_flags & (uint8_t)mask;
|
||||
if (mode > 0)
|
||||
{
|
||||
if (addComma)
|
||||
{
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data);
|
||||
}
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s"), mqtt_data, sns_opentherm_flag_text(mode));
|
||||
addComma = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sns_opentherm_set_central_heating_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
sns_ot_boiler_status.m_enableCentralHeating = atoi(XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndNumber(sns_ot_boiler_status.m_enableCentralHeating ? 1 : 0);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns69(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
if (FUNC_INIT == function)
|
||||
{
|
||||
if (sns_opentherm_Init())
|
||||
{
|
||||
sns_opentherm_CheckSettings();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sns_ot_master)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case FUNC_LOOP:
|
||||
sns_ot_master->process();
|
||||
break;
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_READY && sns_ot_master->isReady())
|
||||
{
|
||||
unsigned long request = sns_opentherm_get_next_request(&sns_ot_boiler_status);
|
||||
if (-1 != request)
|
||||
{
|
||||
sns_ot_master->sendRequestAync(request);
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_INFLIGHT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_DISCONNECTED)
|
||||
{
|
||||
// If disconnected, wait for the SNS_OT_DISCONNECT_COOLDOWN_SECONDS before the handshake
|
||||
if (sns_ot_disconnect_cooldown == 0)
|
||||
{
|
||||
sns_ot_disconnect_cooldown = SNS_OT_DISCONNECT_COOLDOWN_SECONDS;
|
||||
}
|
||||
else if (--sns_ot_disconnect_cooldown == 0)
|
||||
{
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING;
|
||||
}
|
||||
}
|
||||
else if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_CONNECTING)
|
||||
{
|
||||
sns_ot_start_handshake();
|
||||
}
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kOpenThermCommands, OpenThermCommands);
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
sns_opentherm_stat(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
sns_opentherm_stat(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_OPENTHERM
|
441
tasmota/xsns_69_opentherm_protocol.ino
Normal file
441
tasmota/xsns_69_opentherm_protocol.ino
Normal file
@ -0,0 +1,441 @@
|
||||
/*
|
||||
xsns_69_opentherm_protocol.ino - OpenTherm protocol support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Yuriy Sannikov
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_OPENTHERM
|
||||
|
||||
#include "OpenTherm.h"
|
||||
|
||||
// Temperature tolerance. If temperature setpoint difference is less than the value,
|
||||
// OT (1)(Control setpoint) command will be skipped
|
||||
#define OPENTHERM_BOILER_SETPOINT_TOLERANCE 1.0
|
||||
|
||||
typedef union {
|
||||
uint8_t m_flags;
|
||||
struct
|
||||
{
|
||||
uint8_t notSupported : 1; // If set, boiler does not support this command
|
||||
uint8_t supported : 1; // Set if at least one response were successfull
|
||||
uint8_t retryCount : 2; // Retry counter before notSupported flag being set
|
||||
};
|
||||
} OpenThermParamFlags;
|
||||
|
||||
typedef union {
|
||||
float m_float;
|
||||
uint8_t m_u8;
|
||||
uint16_t m_u16;
|
||||
unsigned long m_ul;
|
||||
bool m_bool;
|
||||
} ResponseStorage;
|
||||
|
||||
typedef struct OpenThermCommandT
|
||||
{
|
||||
const char *m_command_name;
|
||||
uint8_t m_command_code;
|
||||
OpenThermParamFlags m_flags;
|
||||
ResponseStorage m_results[2];
|
||||
unsigned long (*m_ot_make_request)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus);
|
||||
void (*m_ot_parse_response)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus, unsigned long response);
|
||||
void (*m_ot_appent_telemetry)(OpenThermCommandT *self);
|
||||
} OpenThermCommand;
|
||||
|
||||
OpenThermCommand sns_opentherm_commands[] = {
|
||||
{// Get/Set Slave Status Flags
|
||||
.m_command_name = "SLAVE",
|
||||
.m_command_code = 0,
|
||||
// OpenTherm ID(0) should never go into the notSupported state due to some connectivity issues
|
||||
// otherwice it may lose boiler control
|
||||
.m_flags = {.supported = 1},
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_set_slave_flags,
|
||||
.m_ot_parse_response = sns_opentherm_parse_slave_flags,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_slave_flags},
|
||||
{// Set boiler temperature
|
||||
.m_command_name = "BTMP",
|
||||
.m_command_code = 0,
|
||||
// OpenTherm ID(1) also should never go into the notSupported state due to some connectivity issues
|
||||
.m_flags = {.supported = 1},
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_set_boiler_temperature,
|
||||
.m_ot_parse_response = sns_opentherm_parse_set_boiler_temperature,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_boiler_temperature},
|
||||
{// Set Hot Water temperature
|
||||
.m_command_name = "HWTMP",
|
||||
.m_command_code = 0,
|
||||
// OpenTherm ID(56) may not be supported
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_set_boiler_dhw_temperature,
|
||||
.m_ot_parse_response = sns_opentherm_parse_boiler_dhw_temperature,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_boiler_dhw_temperature},
|
||||
{// Read Application-specific fault flags and OEM fault code
|
||||
.m_command_name = "ASFF",
|
||||
.m_command_code = 0,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_flags,
|
||||
.m_ot_parse_response = sns_opentherm_parse_flags,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_flags},
|
||||
{// Read An OEM-specific diagnostic/service code
|
||||
.m_command_name = "OEMD",
|
||||
.m_command_code = 0,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_oem_diag,
|
||||
.m_ot_parse_response = sns_opentherm_parse_oem_diag,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_oem_diag},
|
||||
{// Read Flame modulation
|
||||
.m_command_name = "FLM",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::RelModLevel,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_flame_modulation,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read Boiler Temperature
|
||||
.m_command_name = "TB",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Tboiler,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_boiler_temperature,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read DHW temperature
|
||||
.m_command_name = "TDHW",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Tdhw,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read Outside temperature
|
||||
.m_command_name = "TOUT",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Toutside,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read Return water temperature
|
||||
.m_command_name = "TRET",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Tret,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read DHW setpoint
|
||||
.m_command_name = "DHWS",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::TdhwSet,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_dhw_setpoint,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read max CH water setpoint
|
||||
.m_command_name = "TMAX",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::MaxTSet,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////// Process Slave Status Flags & Control //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_set_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status)
|
||||
{
|
||||
bool centralHeatingIsOn = status->m_enableCentralHeating;
|
||||
|
||||
if (status->m_useDiagnosticIndicationAsHeatRequest) {
|
||||
centralHeatingIsOn |= OpenTherm::isDiagnostic(status->m_slave_raw_status);
|
||||
}
|
||||
|
||||
if (self->m_results[1].m_bool != centralHeatingIsOn) {
|
||||
AddLog_P2(LOG_LEVEL_INFO,
|
||||
PSTR("[OTH]: Central Heating transitioning from %s to %s"),
|
||||
self->m_results[1].m_bool ? "on" : "off",
|
||||
status->m_enableCentralHeating ? "on" : "off");
|
||||
}
|
||||
self->m_results[1].m_bool = centralHeatingIsOn;
|
||||
|
||||
unsigned int data = centralHeatingIsOn |
|
||||
(status->m_enableHotWater << 1) |
|
||||
(status->m_enableCooling << 2) |
|
||||
(status->m_enableOutsideTemperatureCompensation << 3) |
|
||||
(status->m_enableCentralHeating2 << 4);
|
||||
|
||||
data <<= 8;
|
||||
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Status, data);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
boilerStatus->m_slave_raw_status = response;
|
||||
self->m_results[0].m_ul = response;
|
||||
}
|
||||
|
||||
#define OT_FLAG_TO_ON_OFF(status, flag) ((((status) & (flag)) != 0) ? 1 : 0)
|
||||
void sns_opentherm_tele_slave_flags(struct OpenThermCommandT *self)
|
||||
{
|
||||
unsigned long st = self->m_results[0].m_ul;
|
||||
ResponseAppend_P(PSTR("{\"FAULT\":%d,\"CH\":%d,\"DHW\":%d,\"FL\":%d,\"COOL\":%d,\"CH2\":%d,\"DIAG\":%d,\"RAW\":%lu}"),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x01),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x02),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x04),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x08),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x10),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x20),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x40),
|
||||
st);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Set Boiler Temperature //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status)
|
||||
{
|
||||
// Assuming some boilers might write setpoint temperature into the Flash memory
|
||||
// Having PID controlled appliance may produce a lot of small fluctuations in the setpoint value
|
||||
// wearing out Boiler flash memory.
|
||||
float diff = abs(status->m_boilerSetpoint - self->m_results[0].m_float);
|
||||
// Ignore small changes in the boiler setpoint temperature
|
||||
if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_INFO,
|
||||
PSTR("[OTH]: Setting Boiler Temp. Old: %d, New: %d"),
|
||||
(int)self->m_results[0].m_float,
|
||||
(int)status->m_boilerSetpoint);
|
||||
self->m_results[0].m_float = status->m_boilerSetpoint;
|
||||
|
||||
unsigned int data = OpenTherm::temperatureToData(status->m_boilerSetpoint);
|
||||
return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data);
|
||||
}
|
||||
void sns_opentherm_parse_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[1].m_float = OpenTherm::getFloat(response);
|
||||
}
|
||||
void sns_opentherm_tele_boiler_temperature(struct OpenThermCommandT *self)
|
||||
{
|
||||
char requested[FLOATSZ];
|
||||
dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested);
|
||||
char actual[FLOATSZ];
|
||||
dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual);
|
||||
|
||||
// indicate fault if tepmerature demand and actual setpoint are greater then tolerance
|
||||
bool isFault = abs(self->m_results[1].m_float - self->m_results[0].m_float) > OPENTHERM_BOILER_SETPOINT_TOLERANCE;
|
||||
|
||||
ResponseAppend_P(PSTR("{\"FAULT\":%d,\"REQ\":%s,\"ACT\": %s}"),
|
||||
(int)isFault,
|
||||
requested,
|
||||
actual);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Set Domestic Hot Water Temperature //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_set_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status)
|
||||
{
|
||||
// The same consideration as for the boiler temperature
|
||||
float diff = abs(status->m_hotWaterSetpoint - self->m_results[0].m_float);
|
||||
// Ignore small changes in the boiler setpoint temperature
|
||||
if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_INFO,
|
||||
PSTR("[OTH]: Setting Hot Water Temp. Old: %d, New: %d"),
|
||||
(int)self->m_results[0].m_float,
|
||||
(int)status->m_hotWaterSetpoint);
|
||||
|
||||
self->m_results[0].m_float = status->m_hotWaterSetpoint;
|
||||
|
||||
unsigned int data = OpenTherm::temperatureToData(status->m_hotWaterSetpoint);
|
||||
return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data);
|
||||
}
|
||||
void sns_opentherm_parse_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[1].m_float = OpenTherm::getFloat(response);
|
||||
}
|
||||
void sns_opentherm_tele_boiler_dhw_temperature(struct OpenThermCommandT *self)
|
||||
{
|
||||
char requested[FLOATSZ];
|
||||
dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested);
|
||||
char actual[FLOATSZ];
|
||||
dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual);
|
||||
|
||||
ResponseAppend_P(PSTR("{\"REQ\":%s,\"ACT\": %s}"),
|
||||
requested,
|
||||
actual);
|
||||
}
|
||||
|
||||
/////////////////////////////////// App Specific Fault Flags //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_get_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *)
|
||||
{
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
uint8_t fault_code = (response >> 8) & 0xFF;
|
||||
uint8_t oem_fault_code = response & 0xFF;
|
||||
boilerStatus->m_fault_code = fault_code;
|
||||
boilerStatus->m_oem_fault_code = fault_code;
|
||||
self->m_results[0].m_u8 = fault_code;
|
||||
self->m_results[1].m_u8 = oem_fault_code;
|
||||
}
|
||||
|
||||
void sns_opentherm_tele_flags(struct OpenThermCommandT *self)
|
||||
{
|
||||
ResponseAppend_P(PSTR("{\"FC\":%d,\"OFC\":%d}"),
|
||||
(int)self->m_results[0].m_u8,
|
||||
(int)self->m_results[1].m_u8);
|
||||
}
|
||||
|
||||
/////////////////////////////////// OEM Diag Code //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_get_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *)
|
||||
{
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::OEMDiagnosticCode, 0);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
uint16_t diag_code = (uint16_t)response & 0xFFFF;
|
||||
boilerStatus->m_oem_diag_code = diag_code;
|
||||
self->m_results[0].m_u16 = diag_code;
|
||||
}
|
||||
|
||||
void sns_opentherm_tele_oem_diag(struct OpenThermCommandT *self)
|
||||
{
|
||||
ResponseAppend_P(PSTR("%d"), (int)self->m_results[0].m_u16);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Generic Single Float /////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_get_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *)
|
||||
{
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, (OpenThermMessageID)self->m_command_code, 0);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
}
|
||||
|
||||
void sns_opentherm_tele_generic_float(struct OpenThermCommandT *self)
|
||||
{
|
||||
char str[FLOATSZ];
|
||||
dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, str);
|
||||
ResponseAppend_P(PSTR("%s"), str);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Specific Floats Rerports to the /////////////////////////////////////////////////
|
||||
void sns_opentherm_parse_dhw_setpoint(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
boilerStatus->m_hotWaterSetpoint_read = self->m_results[0].m_float;
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_flame_modulation(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
boilerStatus->m_flame_modulation_read = self->m_results[0].m_float;
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
boilerStatus->m_boiler_temperature_read = self->m_results[0].m_float;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define SNS_OT_COMMANDS_COUNT (sizeof(sns_opentherm_commands) / sizeof(OpenThermCommand))
|
||||
int sns_opentherm_current_command = SNS_OT_COMMANDS_COUNT;
|
||||
|
||||
unsigned long sns_opentherm_get_next_request(struct OT_BOILER_STATUS_T *boilerStatus)
|
||||
{
|
||||
// get next and loop the command
|
||||
if (++sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT)
|
||||
{
|
||||
sns_opentherm_current_command = 0;
|
||||
}
|
||||
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command];
|
||||
// Return error if command known as not supported
|
||||
if (cmd->m_flags.notSupported)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Retrurn OT compatible request
|
||||
return cmd->m_ot_make_request(cmd, boilerStatus);
|
||||
}
|
||||
|
||||
void sns_opentherm_check_retry_request()
|
||||
{
|
||||
if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command];
|
||||
|
||||
bool canRetry = ++cmd->m_flags.retryCount < 3;
|
||||
// In case of last retry and if this command never respond successfully, set notSupported flag
|
||||
if (!canRetry && !cmd->m_flags.supported)
|
||||
{
|
||||
cmd->m_flags.notSupported = true;
|
||||
AddLog_P2(LOG_LEVEL_ERROR,
|
||||
PSTR("[OTH]: command %s is not supported by the boiler. Last status: %s"),
|
||||
cmd->m_command_name,
|
||||
sns_ot_master->statusToString(sns_ot_master->getLastResponseStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
void sns_opentherm_process_success_response(struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command];
|
||||
// mark command as supported
|
||||
cmd->m_flags.supported = true;
|
||||
|
||||
cmd->m_ot_parse_response(cmd, boilerStatus, response);
|
||||
}
|
||||
|
||||
void sns_opentherm_dump_telemetry()
|
||||
{
|
||||
bool add_coma = false;
|
||||
for (int i = 0; i < SNS_OT_COMMANDS_COUNT; ++i)
|
||||
{
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[i];
|
||||
if (!cmd->m_flags.supported)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ResponseAppend_P(PSTR("%s\"%s\":"), add_coma ? "," : "", cmd->m_command_name);
|
||||
|
||||
cmd->m_ot_appent_telemetry(cmd);
|
||||
|
||||
add_coma = true;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -202,12 +202,12 @@ a_features = [[
|
||||
],[
|
||||
"USE_KEELOQ","USE_HRXL","USE_SONOFF_D1","USE_HDC1080",
|
||||
"USE_IAQ","USE_DISPLAY_SEVENSEG","USE_AS3935","USE_PING",
|
||||
"USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","",""
|
||||
"","","","USE_WEBCAM"
|
||||
]]
|
||||
|
||||
usage = "usage: decode-status {-d | -f} arg"
|
||||
@ -241,7 +241,7 @@ else:
|
||||
obj = json.load(fp)
|
||||
|
||||
def StartDecode():
|
||||
print ("\n*** decode-status.py v20200428 by Theo Arends and Jacek Ziolkowski ***")
|
||||
print ("\n*** decode-status.py v20200507 by Theo Arends and Jacek Ziolkowski ***")
|
||||
|
||||
# print("Decoding\n{}".format(obj))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user