diff --git a/README.md b/README.md index 3eca78b2b..606577d21 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute! ### Development [![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) -Current version is **5.14.0a** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. +Current version is **5.14.0b** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. ### Quick install Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as documented in the wiki. @@ -22,7 +22,7 @@ Download one of the released binaries from https://github.com/arendst/Sonoff-Tas If you want to compile Sonoff-Tasmota yourself keep in mind the following: - Only Flash Mode **DOUT** is supported. Do not use Flash Mode DIO / QIO / QOUT as it might seem to brick your device. See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Theo's-Tasmota-Tips) for background information. -- Sonoff-Tasmota uses a 1M linker script WITHOUT spiffs for optimal code space. If you compile using ESP/Arduino library 2.3.0 then download the provided new linker script to your Arduino IDE or Platformio base folder. Later version of ESP/Arduino library already contain the correct linker script. See [Wiki > Prerequisite](https://github.com/arendst/Sonoff-Tasmota/wiki/Prerequisite). +- Sonoff-Tasmota uses a 1M linker script WITHOUT spiffs **1M (no SPIFFS)** for optimal code space. If you compile using ESP/Arduino library 2.3.0 then download the provided new linker script to your Arduino IDE or Platformio base folder. Later version of ESP/Arduino library already contain the correct linker script. See [Wiki > Prerequisite](https://github.com/arendst/Sonoff-Tasmota/wiki/Prerequisite). - To make compile time changes to Sonoff-Tasmota it can use the ``user_config_override.h`` file. It assures keeping your settings when you download and compile a new version. To use ``user_config.override.h`` you will have to make a copy of the provided ``user_config.override_sample.h`` file and add your setting overrides. To enable the override file you will need to use a compile define as documented in the ``user_config_override_sample.h`` file. ### Version Information diff --git a/lib/ESPAsyncUDP-master/.travis.yml b/lib/ESPAsyncUDP-master/.travis.yml new file mode 100644 index 000000000..c1ef4ec8f --- /dev/null +++ b/lib/ESPAsyncUDP-master/.travis.yml @@ -0,0 +1,37 @@ +sudo: false +language: bash +os: + - linux + +script: + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz + - tar xf arduino-1.6.5-linux64.tar.xz + - mv arduino-1.6.5 $HOME/arduino_ide + - export PATH="$HOME/arduino_ide:$PATH" + - which arduino + - mkdir -p $HOME/Arduino/libraries + - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/ESPAsyncUDP + - cd $HOME/arduino_ide/hardware + - mkdir esp8266com + - cd esp8266com + - git clone https://github.com/esp8266/Arduino.git esp8266 + - cd esp8266/tools + - python get.py + - source $TRAVIS_BUILD_DIR/travis/common.sh + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketches arduino $HOME/Arduino/libraries/ESPAsyncUDP esp8266 + +notifications: + email: + on_success: change + on_failure: change + webhooks: + urls: + - https://webhooks.gitter.im/e/60e65d0c78ea0a920347 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/lib/ESPAsyncUDP-master/README.md b/lib/ESPAsyncUDP-master/README.md new file mode 100644 index 000000000..997a7cc26 --- /dev/null +++ b/lib/ESPAsyncUDP-master/README.md @@ -0,0 +1,10 @@ +# ESPAsyncUDP +Async UDP Library for ESP8266 Arduino [![Build Status](https://travis-ci.org/me-no-dev/ESPAsyncUDP.svg?branch=master)](https://travis-ci.org/me-no-dev/ESPAsyncUDP) + +[![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +This is a fully asynchronous UDP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP8266 MCUs. + +The library is easy to use and includes support for Unicast, Broadcast and Multicast environments + +Latest GIT version of ESP8266 Arduino might be required for this library to work diff --git a/lib/ESPAsyncUDP-master/examples/AsyncUDPClient/AsyncUDPClient.ino b/lib/ESPAsyncUDP-master/examples/AsyncUDPClient/AsyncUDPClient.ino new file mode 100644 index 000000000..cf528fe12 --- /dev/null +++ b/lib/ESPAsyncUDP-master/examples/AsyncUDPClient/AsyncUDPClient.ino @@ -0,0 +1,51 @@ +#include +#include "ESPAsyncUDP.h" + +const char * ssid = "***********"; +const char * password = "***********"; + +AsyncUDP udp; + +void setup() +{ + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Failed"); + while(1) { + delay(1000); + } + } + if(udp.connect(IPAddress(192,168,1,100), 1234)) { + Serial.println("UDP connected"); + udp.onPacket([](AsyncUDPPacket packet) { + Serial.print("UDP Packet Type: "); + Serial.print(packet.isBroadcast()?"Broadcast":packet.isMulticast()?"Multicast":"Unicast"); + Serial.print(", From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.print(", Data: "); + Serial.write(packet.data(), packet.length()); + Serial.println(); + //reply to the client + packet.printf("Got %u bytes of data", packet.length()); + }); + //Send unicast + udp.print("Hello Server!"); + } +} + +void loop() +{ + delay(1000); + //Send broadcast on port 1234 + udp.broadcastTo("Anyone here?", 1234); +} diff --git a/lib/ESPAsyncUDP-master/examples/AsyncUDPMulticastServer/AsyncUDPMulticastServer.ino b/lib/ESPAsyncUDP-master/examples/AsyncUDPMulticastServer/AsyncUDPMulticastServer.ino new file mode 100644 index 000000000..bb3e69c9b --- /dev/null +++ b/lib/ESPAsyncUDP-master/examples/AsyncUDPMulticastServer/AsyncUDPMulticastServer.ino @@ -0,0 +1,52 @@ +#include +#include "ESPAsyncUDP.h" + +const char * ssid = "***********"; +const char * password = "***********"; + +AsyncUDP udp; + +void setup() +{ + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Failed"); + while(1) { + delay(1000); + } + } + if(udp.listenMulticast(IPAddress(239,1,2,3), 1234)) { + Serial.print("UDP Listening on IP: "); + Serial.println(WiFi.localIP()); + udp.onPacket([](AsyncUDPPacket packet) { + Serial.print("UDP Packet Type: "); + Serial.print(packet.isBroadcast()?"Broadcast":packet.isMulticast()?"Multicast":"Unicast"); + Serial.print(", From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.print(", Data: "); + Serial.write(packet.data(), packet.length()); + Serial.println(); + //reply to the client + packet.printf("Got %u bytes of data", packet.length()); + }); + //Send multicast + udp.print("Hello!"); + } +} + +void loop() +{ + delay(1000); + //Send multicast + udp.print("Anyone here?"); +} diff --git a/lib/ESPAsyncUDP-master/examples/AsyncUDPServer/AsyncUDPServer.ino b/lib/ESPAsyncUDP-master/examples/AsyncUDPServer/AsyncUDPServer.ino new file mode 100644 index 000000000..fc12a7fc3 --- /dev/null +++ b/lib/ESPAsyncUDP-master/examples/AsyncUDPServer/AsyncUDPServer.ino @@ -0,0 +1,50 @@ +#include +#include "ESPAsyncUDP.h" + +const char * ssid = "***********"; +const char * password = "***********"; + +AsyncUDP udp; + +void setup() +{ + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Failed"); + while(1) { + delay(1000); + } + } + if(udp.listen(1234)) { + Serial.print("UDP Listening on IP: "); + Serial.println(WiFi.localIP()); + udp.onPacket([](AsyncUDPPacket packet) { + Serial.print("UDP Packet Type: "); + Serial.print(packet.isBroadcast()?"Broadcast":packet.isMulticast()?"Multicast":"Unicast"); + Serial.print(", From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.print(", Data: "); + Serial.write(packet.data(), packet.length()); + Serial.println(); + //reply to the client + packet.printf("Got %u bytes of data", packet.length()); + }); + } +} + +void loop() +{ + delay(1000); + //Send broadcast + udp.broadcast("Anyone here?"); +} diff --git a/lib/ESPAsyncUDP-master/keywords.txt b/lib/ESPAsyncUDP-master/keywords.txt new file mode 100644 index 000000000..67c0b97a7 --- /dev/null +++ b/lib/ESPAsyncUDP-master/keywords.txt @@ -0,0 +1,33 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +AsyncUDP KEYWORD1 +AsyncUDPPacket KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +connect KEYWORD2 +connected KEYWORD2 +listen KEYWORD2 +listenMulticast KEYWORD2 +close KEYWORD2 +write KEYWORD2 +broadcast KEYWORD2 +onPacket KEYWORD2 +data KEYWORD2 +length KEYWORD2 +localIP KEYWORD2 +localPort KEYWORD2 +remoteIP KEYWORD2 +remotePort KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/lib/ESPAsyncUDP-master/library.json b/lib/ESPAsyncUDP-master/library.json new file mode 100644 index 000000000..fe300b6e6 --- /dev/null +++ b/lib/ESPAsyncUDP-master/library.json @@ -0,0 +1,17 @@ +{ + "name":"ESPAsyncUDP", + "description":"Asynchronous UDP Library for ESP8266", + "keywords":"async,udp,server,client,multicast,broadcast", + "authors": + { + "name": "Hristo Gochkov", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/me-no-dev/ESPAsyncUDP.git" + }, + "frameworks": "arduino", + "platforms":"espressif" +} diff --git a/lib/ESPAsyncUDP-master/library.properties b/lib/ESPAsyncUDP-master/library.properties new file mode 100644 index 000000000..1aa6ef1ad --- /dev/null +++ b/lib/ESPAsyncUDP-master/library.properties @@ -0,0 +1,9 @@ +name=ESP Async UDP +version=1.0.0 +author=Me-No-Dev +maintainer=Me-No-Dev +sentence=Async UDP Library for ESP8266 +paragraph=Async UDP Library for ESP8266 +category=Other +url=https://github.com/me-no-dev/ESPAsyncUDP +architectures=* diff --git a/lib/ESPAsyncUDP-master/src/AsyncUDP.cpp b/lib/ESPAsyncUDP-master/src/AsyncUDP.cpp new file mode 100644 index 000000000..2a538c4c6 --- /dev/null +++ b/lib/ESPAsyncUDP-master/src/AsyncUDP.cpp @@ -0,0 +1,427 @@ +#include "Arduino.h" +#include "ESPAsyncUDP.h" + +extern "C" { +#include "user_interface.h" +#include "lwip/opt.h" +#include "lwip/inet.h" +#include "lwip/udp.h" +#include "lwip/igmp.h" +} + +AsyncUDPMessage::AsyncUDPMessage(size_t size) +{ + _index = 0; + if(size > 1460) { + size = 1460; + } + _size = size; + _buffer = (uint8_t *)malloc(size); +} + +AsyncUDPMessage::~AsyncUDPMessage() +{ + if(_buffer) { + free(_buffer); + } +} + +size_t AsyncUDPMessage::write(const uint8_t *data, size_t len) +{ + if(_buffer == NULL) { + return 0; + } + size_t s = space(); + if(len > s) { + len = s; + } + memcpy(_buffer + _index, data, len); + _index += len; + return len; +} + +size_t AsyncUDPMessage::write(uint8_t data) +{ + return write(&data, 1); +} + +size_t AsyncUDPMessage::space() +{ + if(_buffer == NULL) { + return 0; + } + return _size - _index; +} + +uint8_t * AsyncUDPMessage::data() +{ + return _buffer; +} + +size_t AsyncUDPMessage::length() +{ + return _index; +} + +void AsyncUDPMessage::flush() +{ + _index = 0; +} + + +AsyncUDPPacket::AsyncUDPPacket(AsyncUDP *udp, ip_addr_t *localIp, uint16_t localPort, ip_addr_t *remoteIp, uint16_t remotePort, uint8_t *data, size_t len) +{ + _udp = udp; + _localIp = localIp; + _localPort = localPort; + _remoteIp = remoteIp; + _remotePort = remotePort; + _data = data; + _len = len; +} + +AsyncUDPPacket::~AsyncUDPPacket() +{ + +} + +uint8_t * AsyncUDPPacket::data() +{ + return _data; +} + +size_t AsyncUDPPacket::length() +{ + return _len; +} + +IPAddress AsyncUDPPacket::localIP() +{ + return IPAddress(_localIp->addr); +} + +uint16_t AsyncUDPPacket::localPort() +{ + return _localPort; +} + +IPAddress AsyncUDPPacket::remoteIP() +{ + return IPAddress(_remoteIp->addr); +} + +uint16_t AsyncUDPPacket::remotePort() +{ + return _remotePort; +} + +bool AsyncUDPPacket::isBroadcast() +{ + return _localIp->addr == 0xFFFFFFFF || _localIp->addr == (uint32_t)(0); +} + +bool AsyncUDPPacket::isMulticast() +{ + return ip_addr_ismulticast(_localIp); +} + +size_t AsyncUDPPacket::write(const uint8_t *data, size_t len) +{ + return _udp->writeTo(data, len, _remoteIp, _remotePort); +} + +size_t AsyncUDPPacket::write(uint8_t data) +{ + return write(&data, 1); +} + +size_t AsyncUDPPacket::send(AsyncUDPMessage &message) +{ + return write(message.data(), message.length()); +} + + + +AsyncUDP::AsyncUDP() +{ + _pcb = NULL; + _connected = false; + _handler = NULL; +} + +AsyncUDP::~AsyncUDP() +{ + close(); +} + +AsyncUDP::operator bool() +{ + return _connected; +} + +bool AsyncUDP::connected() +{ + return _connected; +} + +void AsyncUDP::onPacket(AuPacketHandlerFunctionWithArg cb, void * arg) +{ + onPacket(std::bind(cb, arg, std::placeholders::_1)); +} + +void AsyncUDP::onPacket(AuPacketHandlerFunction cb) +{ + _handler = cb; +} + +void AsyncUDP::_recv(udp_pcb *upcb, pbuf *pb, ip_addr_t *addr, uint16_t port) +{ + (void)upcb; // its unused, avoid warning + while(pb != NULL) { + if(_handler) { + uint8_t * data = (uint8_t*)((pb)->payload); + size_t len = pb->len; + + ip_hdr* iphdr = reinterpret_cast(data - UDP_HLEN - IP_HLEN); + ip_addr_t daddr; + daddr.addr = iphdr->dest.addr; + + udp_hdr* udphdr = reinterpret_cast(((uint8_t*)((pb)->payload)) - UDP_HLEN); + uint16_t dport = ntohs(udphdr->dest); + + AsyncUDPPacket packet(this, &daddr, dport, addr, port, data, len); + _handler(packet); + } + + pbuf * this_pb = pb; + pb = pb->next; + this_pb->next = NULL; + pbuf_free(this_pb); + } +} + +#if LWIP_VERSION_MAJOR == 1 +void AsyncUDP::_s_recv(void *arg, udp_pcb *upcb, pbuf *p, ip_addr_t *addr, uint16_t port) +#else +void AsyncUDP::_s_recv(void *arg, udp_pcb *upcb, pbuf *p, const ip_addr_t *addr, uint16_t port) +#endif +{ + reinterpret_cast(arg)->_recv(upcb, p, (ip_addr_t *)addr, port); +} + +bool AsyncUDP::listen(ip_addr_t *addr, uint16_t port) +{ + close(); + _pcb = udp_new(); + if(_pcb == NULL) { + return false; + } + err_t err = udp_bind(_pcb, addr, port); + if(err != ERR_OK) { + close(); + return false; + } + udp_recv(_pcb, &_s_recv, (void *) this); + _connected = true; + return true; +} + +bool AsyncUDP::listenMulticast(ip_addr_t *addr, uint16_t port, uint8_t ttl) +{ + close(); + if(!ip_addr_ismulticast(addr)) { + return false; + } + ip_addr_t multicast_if_addr; + struct ip_info ifIpInfo; + int mode = wifi_get_opmode(); + if(mode & STATION_MODE) { + wifi_get_ip_info(STATION_IF, &ifIpInfo); + multicast_if_addr.addr = ifIpInfo.ip.addr; + } else if (mode & SOFTAP_MODE) { + wifi_get_ip_info(SOFTAP_IF, &ifIpInfo); + multicast_if_addr.addr = ifIpInfo.ip.addr; + } else { + return false; + } + if (igmp_joingroup(&multicast_if_addr, addr)!= ERR_OK) { + return false; + } + if(!listen(IPADDR_ANY, port)) { + return false; + } +#if LWIP_VERSION_MAJOR == 1 + udp_set_multicast_netif_addr(_pcb, multicast_if_addr); +#else + udp_set_multicast_netif_addr(_pcb, &multicast_if_addr); +#endif + udp_set_multicast_ttl(_pcb, ttl); + ip_addr_copy(_pcb->remote_ip, *addr); + _pcb->remote_port = port; + return true; +} + +bool AsyncUDP::connect(ip_addr_t *addr, uint16_t port) +{ + close(); + _pcb = udp_new(); + if(_pcb == NULL) { + return false; + } + err_t err = udp_connect(_pcb, addr, port); + if(err != ERR_OK) { + close(); + return false; + } + udp_recv(_pcb, &_s_recv, (void *) this); + _connected = true; + return true; +} + +void AsyncUDP::close() +{ + if(_pcb != NULL) { + if(_connected) { + udp_disconnect(_pcb); + } + udp_remove(_pcb); + _connected = false; + _pcb = NULL; + } +} + +size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, ip_addr_t *addr, uint16_t port) +{ + if(!_pcb && !connect(addr, port)) { + return 0; + } + if(len > 1460) { + len = 1460; + } + pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if(pbt != NULL) { + uint8_t* dst = reinterpret_cast(pbt->payload); + memcpy(dst, data, len); + err_t err = udp_sendto(_pcb, pbt, addr, port); + pbuf_free(pbt); + if(err < ERR_OK) { + return 0; + } + return len; + } + return 0; +} + +bool AsyncUDP::listen(const IPAddress addr, uint16_t port) +{ + ip_addr_t laddr; + laddr.addr = addr; + return listen(&laddr, port); +} + +bool AsyncUDP::listen(uint16_t port) +{ + return listen(IPAddress((uint32_t)INADDR_ANY), port); +} + +bool AsyncUDP::listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl) +{ + ip_addr_t laddr; + laddr.addr = addr; + return listenMulticast(&laddr, port, ttl); +} + +bool AsyncUDP::connect(const IPAddress addr, uint16_t port) +{ + ip_addr_t daddr; + daddr.addr = addr; + return connect(&daddr, port); +} + +size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port) +{ + ip_addr_t daddr; + daddr.addr = addr; + return writeTo(data, len, &daddr, port); +} + +size_t AsyncUDP::write(const uint8_t *data, size_t len) +{ + //return writeTo(data, len, &(_pcb->remote_ip), _pcb->remote_port); + if(_pcb){ // Patch applied (https://github.com/me-no-dev/ESPAsyncUDP/pull/21) + return writeTo(data, len, &(_pcb->remote_ip), _pcb->remote_port); + } + return 0; +} + +size_t AsyncUDP::write(uint8_t data) +{ + return write(&data, 1); +} + +size_t AsyncUDP::broadcastTo(uint8_t *data, size_t len, uint16_t port) +{ + ip_addr_t daddr; + daddr.addr = 0xFFFFFFFF; + return writeTo(data, len, &daddr, port); +} + +size_t AsyncUDP::broadcastTo(const char * data, uint16_t port) +{ + return broadcastTo((uint8_t *)data, strlen(data), port); +} + +size_t AsyncUDP::broadcast(uint8_t *data, size_t len) +{ + if(_pcb->local_port != 0) { + return broadcastTo(data, len, _pcb->local_port); + } + return 0; +} + +size_t AsyncUDP::broadcast(const char * data) +{ + return broadcast((uint8_t *)data, strlen(data)); +} + + +size_t AsyncUDP::sendTo(AsyncUDPMessage &message, ip_addr_t *addr, uint16_t port) +{ + if(!message) { + return 0; + } + return writeTo(message.data(), message.length(), addr, port); +} + +size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t port) +{ + //if(!message) { + if((!message) || (!_pcb)) { // Patch applied (https://github.com/me-no-dev/ESPAsyncUDP/pull/21) + return 0; + } + return writeTo(message.data(), message.length(), addr, port); +} + +size_t AsyncUDP::send(AsyncUDPMessage &message) +{ + if(!message) { + return 0; + } + return writeTo(message.data(), message.length(), &(_pcb->remote_ip), _pcb->remote_port); +} + +size_t AsyncUDP::broadcastTo(AsyncUDPMessage &message, uint16_t port) +{ + if(!message) { + return 0; + } + return broadcastTo(message.data(), message.length(), port); +} + +size_t AsyncUDP::broadcast(AsyncUDPMessage &message) +{ + if(!message) { + return 0; + } + return broadcast(message.data(), message.length()); +} diff --git a/lib/ESPAsyncUDP-master/src/ESPAsyncUDP.h b/lib/ESPAsyncUDP-master/src/ESPAsyncUDP.h new file mode 100644 index 000000000..8e5a70b21 --- /dev/null +++ b/lib/ESPAsyncUDP-master/src/ESPAsyncUDP.h @@ -0,0 +1,130 @@ +#ifndef ESPASYNCUDP_H +#define ESPASYNCUDP_H + +#include "IPAddress.h" +#include "Print.h" +#include +#include "lwip/init.h" + +class AsyncUDP; +class AsyncUDPPacket; +class AsyncUDPMessage; +struct udp_pcb; +struct pbuf; +#if LWIP_VERSION_MAJOR == 1 +struct ip_addr; +typedef struct ip_addr ip_addr_t; +#else +struct ip4_addr; +typedef struct ip4_addr ip_addr_t; +#endif + +class AsyncUDPMessage : public Print +{ +protected: + uint8_t *_buffer; + size_t _index; + size_t _size; +public: + AsyncUDPMessage(size_t size=1460); + virtual ~AsyncUDPMessage(); + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); + size_t space(); + uint8_t * data(); + size_t length(); + void flush(); + operator bool() + { + return _buffer != NULL; + } +}; + +class AsyncUDPPacket : public Print +{ +protected: + AsyncUDP *_udp; + ip_addr_t *_localIp; + uint16_t _localPort; + ip_addr_t *_remoteIp; + uint16_t _remotePort; + uint8_t *_data; + size_t _len; +public: + AsyncUDPPacket(AsyncUDP *udp, ip_addr_t *localIp, uint16_t localPort, ip_addr_t *remoteIp, uint16_t remotePort, uint8_t *data, size_t len); + virtual ~AsyncUDPPacket(); + + uint8_t * data(); + size_t length(); + bool isBroadcast(); + bool isMulticast(); + + IPAddress localIP(); + uint16_t localPort(); + IPAddress remoteIP(); + uint16_t remotePort(); + + size_t send(AsyncUDPMessage &message); + + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); +}; + +typedef std::function AuPacketHandlerFunction; +typedef std::function AuPacketHandlerFunctionWithArg; + +class AsyncUDP : public Print +{ +protected: + udp_pcb *_pcb; + bool _connected; + AuPacketHandlerFunction _handler; + + void _recv(udp_pcb *upcb, pbuf *pb, ip_addr_t *addr, uint16_t port); +#if LWIP_VERSION_MAJOR == 1 + static void _s_recv(void *arg, udp_pcb *upcb, pbuf *p, ip_addr_t *addr, uint16_t port); +#else + static void _s_recv(void *arg, udp_pcb *upcb, pbuf *p, const ip_addr_t *addr, uint16_t port); +#endif + +public: + AsyncUDP(); + virtual ~AsyncUDP(); + + void onPacket(AuPacketHandlerFunctionWithArg cb, void * arg=NULL); + void onPacket(AuPacketHandlerFunction cb); + + bool listen(ip_addr_t *addr, uint16_t port); + bool listen(const IPAddress addr, uint16_t port); + bool listen(uint16_t port); + + bool listenMulticast(ip_addr_t *addr, uint16_t port, uint8_t ttl=1); + bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1); + + bool connect(ip_addr_t *addr, uint16_t port); + bool connect(const IPAddress addr, uint16_t port); + + void close(); + + size_t writeTo(const uint8_t *data, size_t len, ip_addr_t *addr, uint16_t port); + size_t writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port); + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); + + size_t broadcastTo(uint8_t *data, size_t len, uint16_t port); + size_t broadcastTo(const char * data, uint16_t port); + size_t broadcast(uint8_t *data, size_t len); + size_t broadcast(const char * data); + + size_t sendTo(AsyncUDPMessage &message, ip_addr_t *addr, uint16_t port); + size_t sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t port); + size_t send(AsyncUDPMessage &message); + + size_t broadcastTo(AsyncUDPMessage &message, uint16_t port); + size_t broadcast(AsyncUDPMessage &message); + + bool connected(); + operator bool(); +}; + +#endif diff --git a/lib/ESPAsyncUDP-master/travis/common.sh b/lib/ESPAsyncUDP-master/travis/common.sh new file mode 100644 index 000000000..57bede343 --- /dev/null +++ b/lib/ESPAsyncUDP-master/travis/common.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +function build_sketches() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=$(find $srcpath -name *.ino) + for sketch in $sketches; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + echo -e "\n\n ------------ Skipping $sketch ------------ \n\n"; + continue + fi + echo -e "\n\n ------------ Building $sketch ------------ \n\n"; + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($1)" + return $result + fi + done +} diff --git a/lib/esp-knx-ip-0.5.0/DPT.h b/lib/esp-knx-ip-0.5.1/DPT.h similarity index 99% rename from lib/esp-knx-ip-0.5.0/DPT.h rename to lib/esp-knx-ip-0.5.1/DPT.h index 72e0c7f54..71045b103 100644 --- a/lib/esp-knx-ip-0.5.0/DPT.h +++ b/lib/esp-knx-ip-0.5.1/DPT.h @@ -69,4 +69,4 @@ typedef struct __color uint8_t red; uint8_t green; uint8_t blue; -} color_t; \ No newline at end of file +} color_t; diff --git a/lib/esp-knx-ip-0.5.0/LICENSE b/lib/esp-knx-ip-0.5.1/LICENSE similarity index 100% rename from lib/esp-knx-ip-0.5.0/LICENSE rename to lib/esp-knx-ip-0.5.1/LICENSE diff --git a/lib/esp-knx-ip-0.5.0/README.md b/lib/esp-knx-ip-0.5.1/README.md similarity index 100% rename from lib/esp-knx-ip-0.5.0/README.md rename to lib/esp-knx-ip-0.5.1/README.md diff --git a/lib/esp-knx-ip-0.5.0/esp-knx-ip-config.cpp b/lib/esp-knx-ip-0.5.1/esp-knx-ip-config.cpp similarity index 100% rename from lib/esp-knx-ip-0.5.0/esp-knx-ip-config.cpp rename to lib/esp-knx-ip-0.5.1/esp-knx-ip-config.cpp diff --git a/lib/esp-knx-ip-0.5.0/esp-knx-ip-conversion.cpp b/lib/esp-knx-ip-0.5.1/esp-knx-ip-conversion.cpp similarity index 100% rename from lib/esp-knx-ip-0.5.0/esp-knx-ip-conversion.cpp rename to lib/esp-knx-ip-0.5.1/esp-knx-ip-conversion.cpp diff --git a/lib/esp-knx-ip-0.5.0/esp-knx-ip-send.cpp b/lib/esp-knx-ip-0.5.1/esp-knx-ip-send.cpp similarity index 88% rename from lib/esp-knx-ip-0.5.0/esp-knx-ip-send.cpp rename to lib/esp-knx-ip-0.5.1/esp-knx-ip-send.cpp index 72459f922..624e08d18 100644 --- a/lib/esp-knx-ip-0.5.0/esp-knx-ip-send.cpp +++ b/lib/esp-knx-ip-0.5.1/esp-knx-ip-send.cpp @@ -33,10 +33,11 @@ void ESPKNXIP::send(address_t const &receiver, knx_command_type_t ct, uint8_t da cemi_msg->additional_info_len = 0; cemi_service_t *cemi_data = &cemi_msg->data.service_information; cemi_data->control_1.bits.confirm = 0; - cemi_data->control_1.bits.ack = 0; +//cemi_data->control_1.bits.ack = 1; + cemi_data->control_1.bits.ack = 0; // ask for ACK? 0-no 1-yes cemi_data->control_1.bits.priority = B11; cemi_data->control_1.bits.system_broadcast = 0x01; - cemi_data->control_1.bits.repeat = 0x01; + cemi_data->control_1.bits.repeat = 0x01; // 0 = repeated telegram, 1 = not repeated telegram cemi_data->control_1.bits.reserved = 0; cemi_data->control_1.bits.frame_type = 0x01; cemi_data->control_2.bits.extended_frame_format = 0x00; @@ -47,10 +48,13 @@ void ESPKNXIP::send(address_t const &receiver, knx_command_type_t ct, uint8_t da //cemi_data->destination.bytes.high = (area << 3) | line; //cemi_data->destination.bytes.low = member; cemi_data->data_len = data_len; - cemi_data->pci.apci = (ct & 0x0C) >> 2; - cemi_data->pci.tpci_seq_number = 0x00; // ??? - cemi_data->pci.tpci_comm_type = KNX_COT_UDP; // ??? + cemi_data->pci.apci = (ct & 0x0C) >> 2; +//cemi_data->pci.apci = KNX_COT_NCD_ACK; + cemi_data->pci.tpci_seq_number = 0x00; + cemi_data->pci.tpci_comm_type = KNX_COT_UDP; // Type of communication: DATA PACKAGE or CONTROL DATA +//cemi_data->pci.tpci_comm_type = KNX_COT_NCD; // Type of communication: DATA PACKAGE or CONTROL DATA memcpy(cemi_data->data, data, data_len); +//cemi_data->data[0] = (cemi_data->data[0] & 0x3F) | ((KNX_COT_NCD_ACK & 0x03) << 6); cemi_data->data[0] = (cemi_data->data[0] & 0x3F) | ((ct & 0x03) << 6); #if SEND_CHECKSUM @@ -73,9 +77,13 @@ void ESPKNXIP::send(address_t const &receiver, knx_command_type_t ct, uint8_t da DEBUG_PRINTLN(F("")); #endif +#ifdef USE_ASYNC_UDP + udp.writeTo(buf, len, MULTICAST_IP, MULTICAST_PORT); +#else udp.beginPacketMulticast(MULTICAST_IP, MULTICAST_PORT, WiFi.localIP()); udp.write(buf, len); udp.endPacket(); +#endif } void ESPKNXIP::send_1bit(address_t const &receiver, knx_command_type_t ct, uint8_t bit) diff --git a/lib/esp-knx-ip-0.5.0/esp-knx-ip-webserver.cpp b/lib/esp-knx-ip-0.5.1/esp-knx-ip-webserver.cpp similarity index 100% rename from lib/esp-knx-ip-0.5.0/esp-knx-ip-webserver.cpp rename to lib/esp-knx-ip-0.5.1/esp-knx-ip-webserver.cpp diff --git a/lib/esp-knx-ip-0.5.0/esp-knx-ip.cpp b/lib/esp-knx-ip-0.5.1/esp-knx-ip.cpp similarity index 97% rename from lib/esp-knx-ip-0.5.0/esp-knx-ip.cpp rename to lib/esp-knx-ip-0.5.1/esp-knx-ip.cpp index b572333b9..767fa9752 100644 --- a/lib/esp-knx-ip-0.5.0/esp-knx-ip.cpp +++ b/lib/esp-knx-ip-0.5.1/esp-knx-ip.cpp @@ -96,7 +96,12 @@ void ESPKNXIP::__start() server->begin(); } +#ifdef USE_ASYNC_UDP + udp.listenMulticast(MULTICAST_IP, MULTICAST_PORT); + udp.onPacket([this](AsyncUDPPacket &packet) { __loop_knx(packet); }); +#else udp.beginMulticast(WiFi.localIP(), MULTICAST_IP, MULTICAST_PORT); +#endif } void ESPKNXIP::save_to_eeprom() @@ -511,7 +516,9 @@ feedback_id_t ESPKNXIP::feedback_register_action(String name, feedback_action_fp void ESPKNXIP::loop() { + #ifndef USE_ASYNC_UDP __loop_knx(); + #endif if (server != nullptr) { __loop_webserver(); @@ -523,9 +530,16 @@ void ESPKNXIP::__loop_webserver() server->handleClient(); } +#ifdef USE_ASYNC_UDP +void ESPKNXIP::__loop_knx(AsyncUDPPacket &packet) +{ + size_t read = packet.length(); +#else void ESPKNXIP::__loop_knx() { int read = udp.parsePacket(); +#endif + if (!read) { return; @@ -534,19 +548,31 @@ void ESPKNXIP::__loop_knx() DEBUG_PRINT(F("LEN: ")); DEBUG_PRINTLN(read); +#ifdef USE_ASYNC_UDP + uint8_t *buf = packet.data(); +#else uint8_t buf[read]; - udp.read(buf, read); udp.flush(); +#endif DEBUG_PRINT(F("Got packet:")); + #ifdef ESP_KNX_DEBUG + +#ifdef USE_ASYNC_UDP + for (size_t i = 0; i < read; ++i) +#else for (int i = 0; i < read; ++i) +#endif + { DEBUG_PRINT(F(" 0x")); DEBUG_PRINT(buf[i], 16); } + #endif + DEBUG_PRINTLN(F("")); knx_ip_pkt_t *knx_pkt = (knx_ip_pkt_t *)buf; diff --git a/lib/esp-knx-ip-0.5.0/esp-knx-ip.h b/lib/esp-knx-ip-0.5.1/esp-knx-ip.h similarity index 93% rename from lib/esp-knx-ip-0.5.0/esp-knx-ip.h rename to lib/esp-knx-ip-0.5.1/esp-knx-ip.h index 538264b3e..d8fde1dc9 100644 --- a/lib/esp-knx-ip-0.5.0/esp-knx-ip.h +++ b/lib/esp-knx-ip-0.5.1/esp-knx-ip.h @@ -7,6 +7,14 @@ #ifndef ESP_KNX_IP_H #define ESP_KNX_IP_H +//#define USE_ASYNC_UDP // UDP WIFI Library Selection for Multicast + // If commented out, the esp-knx-ip library will use WIFI_UDP Library that is compatible with ESP8266 Library Version 2.3.0 and up + // If not commented out, the esp-knx-ip library will use ESPAsyncUDP Library that is compatible with ESP8266 Library Version 2.4.0 and up + // The ESPAsyncUDP Library have a more reliable multicast communication + // Please Use it with Patch (https://github.com/me-no-dev/ESPAsyncUDP/pull/21) ) + // check line 57 on esp-knx-ip.h file is uncommented: #include + // Comment out that line when using UDP WIFI to avoid compiling issues on PlatformIO with ESP8266 Library Version 2.3.0 + /** * CONFIG * All MAX_ values must not exceed 255 (1 byte, except MAC_CONFIG_SPACE which can go up to 2 bytes, so 0xffff in theory) and must not be negative! @@ -25,8 +33,8 @@ #define ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS 1 // [Default 0] Set to 1 to always test all assigned callbacks. This allows for multiple callbacks being assigned to the same address. If disabled, only the first assigned will be called. // Webserver related -#define USE_BOOTSTRAP 1 // [Default 1] Set to 1 to enable use of bootstrap CSS for nicer webconfig. CSS is loaded from bootstrapcdn.com. Set to 0 to disable -#define ROOT_PREFIX "" // [Default ""] This gets prepended to all webserver paths, default is empty string "". Set this to "/knx" if you want the config to be available on http:///knx +#define USE_BOOTSTRAP 0 // [Default 1] Set to 1 to enable use of bootstrap CSS for nicer webconfig. CSS is loaded from bootstrapcdn.com. Set to 0 to disable +#define ROOT_PREFIX "/knx" // [Default ""] This gets prepended to all webserver paths, default is empty string "". Set this to "/knx" if you want the config to be available on http:///knx #define DISABLE_EEPROM_BUTTONS 1 // [Default 0] Set to 1 to disable the EEPROM buttons in the web ui. #define DISABLE_REBOOT_BUTTON 1 // [Default 0] Set to 1 to disable the reboot button in the web ui. #define DISABLE_RESTORE_BUTTON 1 // [Default 0] Set to 1 to disable the "restore defaults" button in the web ui. @@ -45,7 +53,13 @@ #include "Arduino.h" #include #include + +#ifdef USE_ASYNC_UDP +//#include +#else #include +#endif + #include #include "DPT.h" @@ -157,6 +171,14 @@ typedef enum __knx_communication_type { KNX_COT_NCD = 0x03, // Numbered Control Data } knx_communication_type_t; +/** + * acpi for KNX_COT_NCD + */ +typedef enum __knx_cot_ncd_ack_type { + KNX_COT_NCD_ACK = 0x10, // Inform positively reception of the Previouly received telegram + KNX_COT_NCD_NACK = 0x11, // Inform negatively reception of the Previouly received telegram +} knx_cot_ncd_ack_type_t; + /** * KNX/IP header */ @@ -217,7 +239,7 @@ typedef struct __cemi_service uint8_t ack:1; // 0 = no ack, 1 = ack uint8_t priority:2; // 0 = system, 1 = high, 2 = urgent/alarm, 3 = normal uint8_t system_broadcast:1; // 0 = system broadcast, 1 = broadcast - uint8_t repeat:1; // 0 = repeat on error, 1 = do not repeat + uint8_t repeat:1; // 0 = repeated telegram, 1 = not repeated telegram uint8_t reserved:1; // always zero uint8_t frame_type:1; // 0 = extended, 1 = standard } bits; @@ -509,7 +531,12 @@ class ESPKNXIP { private: void __start(); + +#ifdef USE_ASYNC_UDP + void __loop_knx(AsyncUDPPacket &packet); +#else void __loop_knx(); +#endif // Webserver functions void __loop_webserver(); @@ -544,7 +571,12 @@ class ESPKNXIP { ESP8266WebServer *server; address_t physaddr; + +#ifdef USE_ASYNC_UDP + AsyncUDP udp; +#else WiFiUDP udp; +#endif callback_assignment_id_t registered_callback_assignments; callback_assignment_id_t free_callback_assignment_slots; diff --git a/lib/esp-knx-ip-0.5.0/examples/environment-sensor/environment-sensor.ino b/lib/esp-knx-ip-0.5.1/examples/environment-sensor/environment-sensor.ino similarity index 100% rename from lib/esp-knx-ip-0.5.0/examples/environment-sensor/environment-sensor.ino rename to lib/esp-knx-ip-0.5.1/examples/environment-sensor/environment-sensor.ino diff --git a/lib/esp-knx-ip-0.5.0/examples/sonoff/sonoff.ino b/lib/esp-knx-ip-0.5.1/examples/sonoff/sonoff.ino similarity index 100% rename from lib/esp-knx-ip-0.5.0/examples/sonoff/sonoff.ino rename to lib/esp-knx-ip-0.5.1/examples/sonoff/sonoff.ino diff --git a/lib/esp-knx-ip-0.5.0/examples/static-config/static-config.ino b/lib/esp-knx-ip-0.5.1/examples/static-config/static-config.ino similarity index 100% rename from lib/esp-knx-ip-0.5.0/examples/static-config/static-config.ino rename to lib/esp-knx-ip-0.5.1/examples/static-config/static-config.ino diff --git a/lib/esp-knx-ip-0.5.0/keywords.txt b/lib/esp-knx-ip-0.5.1/keywords.txt similarity index 100% rename from lib/esp-knx-ip-0.5.0/keywords.txt rename to lib/esp-knx-ip-0.5.1/keywords.txt diff --git a/lib/esp-knx-ip-0.5.0/library.properties b/lib/esp-knx-ip-0.5.1/library.properties similarity index 95% rename from lib/esp-knx-ip-0.5.0/library.properties rename to lib/esp-knx-ip-0.5.1/library.properties index 57cad993c..1adbc402a 100644 --- a/lib/esp-knx-ip-0.5.0/library.properties +++ b/lib/esp-knx-ip-0.5.1/library.properties @@ -1,5 +1,5 @@ name=ESP KNX IP Library -version=0.5 +version=0.5.1 author=Nico Weichbrodt maintainer=Nico Weichbrodt sentence=ESP8266 library for KNX/IP communication. diff --git a/platformio.ini b/platformio.ini index 233f36553..bc5dfca12 100644 --- a/platformio.ini +++ b/platformio.ini @@ -33,11 +33,15 @@ env_default = sonoff ;env_default = sonoff-TW [common] ; ************************************************************ -; *** Esp8266 core for Arduino version -platform = espressif8266@1.5.0 ; v2.3.0 -;platform = espressif8266@1.6.0 ; v2.4.0 -;platform = espressif8266@1.7.0 ; v2.4.1 +; *** Esp8266 core for Arduino version 2.3.0 +platform = espressif8266@1.5.0 +; *** Esp8266 core for Arduino version 2.4.0 +;platform = espressif8266@1.6.0 +; *** Esp8266 core for Arduino version 2.4.1 +;platform = espressif8266@1.7.0 +; *** Esp8266 core for Arduino version latest beta ;platform = https://github.com/platformio/platform-espressif8266.git#feature/stage +; *** Esp8266 core for Arduino current version ;platform = espressif8266 framework = arduino @@ -50,20 +54,19 @@ build_unflags = -Wall build_flags = -Wl,-Tesp8266.flash.1m0.ld -DVTABLES_IN_FLASH - -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY + -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH ; -DUSE_CONFIG_OVERRIDE -; *** Fix Esp/Arduino core 2.4.x induced Tasmota unused floating point includes -extra_scripts = pio/strip-floats.py - ; *** Serial Monitor options -monitor_baud = 115200 +monitor_speed = 115200 ; *** Upload Serial reset method for Wemos and NodeMCU ;upload_speed = 115200 upload_speed = 512000 upload_resetmethod = nodemcu upload_port = COM5 +; *** Fix Esp/Arduino core 2.4.x induced Tasmota unused floating point includes +extra_scripts = pio/strip-floats.py ; *** Upload file to OTA server using SCP ;upload_port = user@host:/path @@ -82,7 +85,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_resetmethod = ${common.upload_resetmethod} upload_port = ${common.upload_port} @@ -95,7 +98,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DBE_MINIMAL -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -107,7 +110,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DUSE_CLASSIC -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -119,7 +122,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DUSE_KNX_NO_EMULATION -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -131,7 +134,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DUSE_ALL_SENSORS -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -143,7 +146,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=bg-BG -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -155,7 +158,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=pt-BR -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -167,7 +170,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=zh-CN -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -179,7 +182,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=cs-CZ -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -191,7 +194,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=de-DE -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -203,7 +206,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=es-AR -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -215,7 +218,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=fr-FR -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -227,7 +230,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=el-GR -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -239,7 +242,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=hu-HU -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -251,7 +254,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=it-IT -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -263,7 +266,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=nl-NL -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -275,7 +278,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=pl-PL -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -287,7 +290,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=pt-PT -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -299,7 +302,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=ru-RU -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} @@ -311,7 +314,7 @@ board = ${common.board} board_flash_mode = ${common.board_flash_mode} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} -DMY_LANGUAGE=zh-TW -monitor_baud = ${common.monitor_baud} +monitor_speed = ${common.monitor_speed} upload_speed = ${common.upload_speed} upload_port = ${common.upload_port} extra_scripts = ${common.extra_scripts} diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index dafa7a55a..59db833a6 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,5 +1,24 @@ -/* 5.14.0a - * Updated to latest release +/* 5.14.0b + * Add two rule sets of 511 characters using commands rule1, rule2 and rule3 + * Add rule support for IrReceive and RfReceive (#2758) + * + * 5.14.0a + * Add feature information to Status 4 + * Add tools folder with python script decode-status.py for decoding some status fields like SetOption and Features + * Add Eastron SDM630 energy meter by Gennaro Tortone (#2735) + * Add KNX communication enhancement by Adrian Scillato (#2742) + * Add KNX energy data by Adrian Scillato (#2750) + * Add python script fw-server.py in tools folder to create a simple OTA server by Gennaro Tortone (#2759) + * Add rules %mem1% to %mem5% variable names storing data in flash (#2780) + * Add rules test on %varx% or %memx% (#2780) + * Add optional token %id% substituting the unique MAC address to fulltopic by Michael Graf (#2794) + * Fix display selection of un-available GPIO options in Module Configuration webpage (#2718) + * Fix timer re-trigger within one minute after restart (#2744) + * Fix IRSend not accepting data value of 0 by David Conran (#2751) + * Fix vars on rules by Adrian Scillato (#2769) + * Fix bug in KNX menu by Adrian Scillato (#2770) + * Fix anomalies in rules (#2778) + * Fix HUE bridge V1 software version by Heiko Krupp (#2788) * * 5.14.0 20180515 * Update language files @@ -14,13 +33,13 @@ * Fix KNX config error (#2628) * Fix sensor MHZ-19 vanishing data over time (#2659) * Fix KNX reconnection issue (#2679) - * Fix DST and STD time for Southern Hemisphere (#2684, #2714) + * Fix DST and STD time for Southern Hemisphere by Adrian Scillato (#2684, #2714) * Add Portuguese in Brazil language file * Add SetOption26 to enforce use of indexes even when only one relay is present (#1055) * Add support for sensor SI1145 UV Index / IR / Visible light (#2496) * Add rule state test for On/Off in addition to 0/1 (#2613) * Add hardware serial option to MHZ-19 sensor (#2659) - * Add Eastron SDM120 energy meter (#2694) + * Add Eastron SDM120 energy meter by Gennaro Tortone (#2694) * Add user entry DST/STD using commands TimeStd and TimeDst (See wiki for parameter syntax) (#2721) * * 5.13.1 20180501 diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 365fee826..0890c5738 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -57,6 +57,7 @@ #define D_JSON_EVERY "Every" #define D_JSON_FAILED "Failed" #define D_JSON_FALLBACKTOPIC "FallbackTopic" +#define D_JSON_FEATURES "Features" #define D_JSON_FLASHMODE "FlashMode" #define D_JSON_FLASHSIZE "FlashSize" #define D_JSON_FREEMEMORY "Free" @@ -391,6 +392,9 @@ #define INCLUDE_FILE(x) QUOTEME(language/x.h) #include INCLUDE_FILE(MY_LANGUAGE) #endif +#ifndef LANGUAGE_LCID + #define LANGUAGE_LCID 2057 // en-GB +#endif // Common enum UnitNames { diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 4e4bbae5b..55482d55c 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -28,11 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.13.1c + * Updated until v5.14.0a \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1026 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "bg" @@ -109,7 +110,7 @@ #define D_LWT "LWT" #define D_MODULE "Модул" #define D_MQTT "MQTT" -#define D_MULTI_PRESS "многократно натискане" +#define D_MULTI_PRESS "множествено натискане" #define D_NOISE "Шум" #define D_NONE "Няма" #define D_OFF "Изкл." @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Друго" #define D_SENT_TO "изпратен до" #define D_KNX_WARNING "Груповият адрес ( 0 / 0 / 0 ) е резервиран и не може да бъде използван." +#define D_KNX_ENHANCEMENT "Подобрена комуникация" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Използвана енергия днес" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 73e2098c5..9f939534a 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1029 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "cs" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Jiné" #define D_SENT_TO "pošli" #define D_KNX_WARNING "Skupinová adresa ( 0 / 0 / 0 ) je rezervována a nemůže být použita." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Spotřeba Dnes" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 35a9f3dad..7d2e7285d 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1031 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "de" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energie heute" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index e23f629b0..97eefcd7b 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1032 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "el" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Αλλο" #define D_SENT_TO "αποστολή σε" #define D_KNX_WARNING "Η Ομάδα Διευθύνσεων ( 0 / 0 / 0 ) είναι δεσμευμένη και δεν μπορεί να χρησιμοποιηθεί." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energy Σήμερα" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 4f97a5eef..9eb8c9d4a 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -28,11 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.12.0n + * Updated until v5.14.0a \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 2057 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "en" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energy Today" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 15dac69ab..aff7b2665 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -28,11 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.13.1c + * Updated until v5.14.0a \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 11274 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "es" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Otro" #define D_SENT_TO "enviada a" #define D_KNX_WARNING "La dirección de grupo ( 0 / 0 / 0 ) está reservada y no puede ser utilizada." +#define D_KNX_ENHANCEMENT "Mejora de Comunicación" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energía Hoy" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 26cee81e9..f37d3cfae 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -28,11 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.12.0m + * Updated until v5.14.0a \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1036 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "fr" @@ -91,7 +92,7 @@ #define D_FALSE "Faux" #define D_FILE "Fichier" #define D_FREE_MEMORY "Mémoire libre" -#define D_FREQUENCY "Frequency" +#define D_FREQUENCY "Fréquence" #define D_GAS "Gaz" #define D_GATEWAY "Passerelle" #define D_GROUP "Groupe" @@ -102,7 +103,7 @@ #define D_IMMEDIATE "immédiat" // Button immediate #define D_INDEX "Index" #define D_INFO "Info" -#define D_INFRARED "Infrared" +#define D_INFRARED "Infra-rouge" #define D_INITIALIZED "Initialisé" #define D_IP_ADDRESS "Adresse IP" #define D_LIGHT "Lumière" @@ -121,9 +122,9 @@ #define D_PORT "Port" #define D_POWER_FACTOR "Facteur de puissance" #define D_POWERUSAGE "Puissance" -#define D_POWERUSAGE_ACTIVE "Active Power" -#define D_POWERUSAGE_APPARENT "Apparent Power" -#define D_POWERUSAGE_REACTIVE "Reactive Power" +#define D_POWERUSAGE_ACTIVE "Puissance Active" +#define D_POWERUSAGE_APPARENT "Puissance Apparente" +#define D_POWERUSAGE_REACTIVE "Puissance Réactive" #define D_PRESSURE "Pression" #define D_PRESSUREATSEALEVEL "PressionMer" #define D_PROGRAM_FLASH_SIZE "Taille Flash Programme" @@ -159,7 +160,7 @@ #define D_UPTIME "Durée d'activité" #define D_USER "Utilisateur" #define D_UTC_TIME "UTC" -#define D_UV_INDEX "Index UV" +#define D_UV_INDEX "Indice UV" #define D_UV_LEVEL "Niveau UV" #define D_VERSION "Version" #define D_VOLTAGE "Tension" @@ -220,7 +221,7 @@ #define D_DEVICE_WILL_RESTART "Le module va redémarrer dans quelques secondes" #define D_BUTTON_TOGGLE "on/off" #define D_CONFIGURATION "Configuration" -#define D_INFORMATION "Information" +#define D_INFORMATION "Informations" #define D_FIRMWARE_UPGRADE "Mise à jour du Firmware" #define D_CONSOLE "Console" #define D_CONFIRM_RESTART "Confirmer redémarrage" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Autre" #define D_SENT_TO "envoyé à" #define D_KNX_WARNING "L'Adresse de Groupe ( 0 / 0 / 0 ) est réservée et ne peut être utilisée." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xsns_03_energy.ino #define D_ENERGY_TODAY "Énergie aujourd'hui" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 5ecab817b..4ebeffbf6 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1038 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "hu" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Mai Energia" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 91808c973..16c11645b 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -28,11 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.13.1a + * Updated until v5.14.0 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1040 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "it" @@ -75,7 +76,7 @@ #define D_DARKLIGHT "Scuro" #define D_DEBUG "Debug" #define D_DISABLED "Disabilitato" -#define D_DISTANCE "Distance" +#define D_DISTANCE "Distanza" #define D_DNS_SERVER "DNS Server" #define D_DONE "Fatto" #define D_DST_TIME "DST" @@ -145,14 +146,14 @@ #define D_SUBNET_MASK "Maschera sottorete" #define D_SUBSCRIBE_TO "Sottoscrivi a" #define D_SUCCESSFUL "Riuscito" -#define D_SUNRISE "Sunrise" -#define D_SUNSET "Sunset" +#define D_SUNRISE "Alba" +#define D_SUNSET "Tramonto" #define D_TEMPERATURE "Temperatura" #define D_TO "a" #define D_TOGGLE "Toggle" #define D_TOPIC "Topic" #define D_TRANSMIT "Trasmesso" -#define D_TRUE "True" +#define D_TRUE "Vero" #define D_TVOC "TVOC" #define D_UPGRADE "aggiornamento" #define D_UPLOAD "Invio" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Altro" #define D_SENT_TO "invia a" #define D_KNX_WARNING "L'indirizzo del gruppo ( 0 / 0 / 0 ) è riservato e non può essere usato." +#define D_KNX_ENHANCEMENT "Miglioramento Comunicazione" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energia Oggi" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 86125053e..262daee7b 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -28,11 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v5.13.1c + * Updated until v5.14.0a \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1043 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "nl" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Overige" #define D_SENT_TO "verzend naar" #define D_KNX_WARNING "Groep adres (0/0/0) is gereserveerd en mag niet worden gebruikt." +#define D_KNX_ENHANCEMENT "Verbeter verbinding" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Verbruik vandaag" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 688c3507e..683d43cb3 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1045 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "pl" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energia Dzisiaj" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 6bf9a6211..02bdec2e4 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1046 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "pt" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Outros" #define D_SENT_TO "Enviar para" #define D_KNX_WARNING "O endereço ( 0 / 0 / 0 ) é reservado e não pode ser usado." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Consumo energético de hoje" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 68d7cd1c5..4cf9e0da3 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 2070 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "pt" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Consumo energético de hoje" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 85540343d..3336484e9 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1049 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "ru" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Энергия Сегодня" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 853c68389..1c3ee0bb7 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 2052 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "zh" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "今日用电量" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 5986dc804..e8505010f 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -33,6 +33,7 @@ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +#define LANGUAGE_LCID 1028 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "zh" @@ -400,6 +401,7 @@ #define D_KNX_COMMAND_OTHER "Other" #define D_SENT_TO "sent to" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" // xdrv_03_energy.ino #define D_ENERGY_TODAY "今日用電量" @@ -461,6 +463,8 @@ #define D_SENSOR_SR04_ECHO "SR04 Ech" #define D_SENSOR_SDM120_TX "SDM120 Tx" #define D_SENSOR_SDM120_RX "SDM120 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/settings.h b/sonoff/settings.h index ec156c85a..0c4a8617c 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -48,11 +48,11 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t not_power_linked : 1; // bit 20 (v5.11.1f) uint32_t no_power_on_check : 1; // bit 21 (v5.11.1i) uint32_t mqtt_serial : 1; // bit 22 (v5.12.0f) - uint32_t rules_enabled : 1; // bit 23 (v5.12.0j) - uint32_t rules_once : 1; // bit 24 (v5.12.0k) + uint32_t rules_enabled : 1; // bit 23 (v5.12.0j) - free since v5.14.0b + uint32_t rules_once : 1; // bit 24 (v5.12.0k) - free since v5.14.0b uint32_t knx_enabled : 1; // bit 25 (v5.12.0l) KNX uint32_t device_index_enable : 1; // bit 26 (v5.13.1a) - uint32_t spare27 : 1; + uint32_t knx_enable_enhancement : 1; // bit 27 (v5.14.0a) KNX uint32_t spare28 : 1; uint32_t spare29 : 1; uint32_t spare30 : 1; @@ -232,9 +232,8 @@ struct SYSCFG { uint8_t light_color[5]; // 498 uint8_t light_correction; // 49D uint8_t light_dimmer; // 49E - - byte free_49F[2]; // 49F - + uint8_t rule_enabled; // 49F + uint8_t rule_once; // 4A0 uint8_t light_fade; // 4A1 uint8_t light_speed; // 4A2 uint8_t light_scheme; // 4A3 @@ -271,11 +270,14 @@ struct SYSCFG { byte knx_GA_param[MAX_KNX_GA]; // 6E2 Type of Input (relay changed, button pressed, sensor read <-teleperiod) byte knx_CB_param[MAX_KNX_CB]; // 6EC Type of Output (set relay, toggle relay, reply sensor value) - byte free_6f6[266]; // 6F6 + byte free_6f6[216]; // 6F6 - char rules[MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m + char mems[RULES_MAX_MEMS][10]; // 7CE + // 800 Full - no more free locations - // A00 - FFF free locations + char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b + + // E00 - FFF free locations } Settings; struct RTCMEM { @@ -286,6 +288,7 @@ struct RTCMEM { unsigned long energy_kWhtotal; // 008 unsigned long pulse_counter[MAX_COUNTERS]; // 00C power_t power; // 01C + // 020 next free location } RtcSettings; struct TIME_T { diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 2d3f3c3d8..170087767 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -143,6 +143,7 @@ extern "C" uint32_t _SPIFFS_end; uint32_t settings_hash = 0; uint32_t settings_location = SETTINGS_LOCATION; +uint8_t *settings_buffer = NULL; /********************************************************************************************/ /* @@ -167,6 +168,24 @@ void SetFlashModeDout() delete[] _buffer; } +void SettingsBufferFree() +{ + if (settings_buffer != NULL) { + free(settings_buffer); + settings_buffer = NULL; + } +} + +bool SettingsBufferAlloc() +{ + SettingsBufferFree(); + if (!(settings_buffer = (uint8_t *)malloc(sizeof(Settings)))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_UPLOAD_ERR_2)); // Not enough (memory) space + return false; + } + return true; +} + uint32_t GetSettingsHash() { uint32_t hash = 0; @@ -870,7 +889,7 @@ void SettingsDelta() Settings.longitude = (int)((double)LONGITUDE * 1000000); } if (Settings.version < 0x050C000B) { - memset(&Settings.rules, 0x00, sizeof(Settings.rules)); + Settings.rules[0][0] = '\0'; } if (Settings.version < 0x050C000D) { memmove(Settings.rules, Settings.rules -256, sizeof(Settings.rules)); // move rules up by 256 bytes @@ -887,6 +906,13 @@ void SettingsDelta() if (Settings.version < 0x050D0103) { SettingsDefaultSet_5_13_1c(); } + if (Settings.version < 0x050E0002) { + for (byte i = 1; i < MAX_RULE_SETS; i++) { + Settings.rules[i][0] = '\0'; + } + Settings.rule_enabled = Settings.flag.rules_enabled; + Settings.rule_once = Settings.flag.rules_once; + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index a2eecea4f..517b755ae 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -50,10 +50,13 @@ typedef unsigned long power_t; // Power (Relay) type #define MAX_DOMOTICZ_SNS_IDX 12 // Max number of Domoticz sensors indices #define MAX_KNX_GA 10 // Max number of KNX Group Addresses to read that can be set #define MAX_KNX_CB 10 // Max number of KNX Group Addresses to write that can be set +#define RULES_MAX_MEMS 5 // Max number of saved vars +#define MAX_RULE_SETS 3 // Max number of rule sets of size 512 characters #define MAX_RULE_SIZE 512 // Max number of characters in rules #define MQTT_TOKEN_PREFIX "%prefix%" // To be substituted by mqtt_prefix[x] #define MQTT_TOKEN_TOPIC "%topic%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic +#define MQTT_TOKEN_ID "%id%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic #define WIFI_HOSTNAME "%s-%04d" // Expands to - @@ -86,7 +89,7 @@ typedef unsigned long power_t; // Power (Relay) type #define SERIALLOG_TIMER 600 // Seconds to disable SerialLog #define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware -#define INPUT_BUFFER_SIZE 512 // Max number of characters in (serial and http) command buffer +#define INPUT_BUFFER_SIZE 520 // Max number of characters in (serial and http) command buffer #define CMDSZ 24 // Max number of characters in command #define TOPSZ 100 // Max number of characters in topic string #define LOGSZ 512 // Max number of characters in log @@ -112,6 +115,9 @@ typedef unsigned long power_t; // Power (Relay) type #define max(a,b) ((a)>(b)?(a):(b)) */ +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + //enum ws2812NeopixelbusFeature { NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_3LED, NEO_RGBW, NEO_GRBW }; // Doesn't work #define NEO_RGB 0 // Neopixel RGB leds #define NEO_GRB 1 // Neopixel GRB leds @@ -131,6 +137,18 @@ typedef unsigned long power_t; // Power (Relay) type #define DAWN_NAUTIC -12.0 #define DAWN_ASTRONOMIC -18.0 +// Sensor definition for KNX Driver +#define KNX_TEMPERATURE 17 +#define KNX_HUMIDITY 18 +#define KNX_ENERGY_VOLTAGE 19 +#define KNX_ENERGY_CURRENT 20 +#define KNX_ENERGY_POWER 21 +#define KNX_ENERGY_POWERFACTOR 22 +#define KNX_ENERGY_DAILY 23 +#define KNX_ENERGY_START 24 +#define KNX_ENERGY_TOTAL 25 +#define KNX_MAX_device_param 25 + /*********************************************************************************************\ * Enumeration \*********************************************************************************************/ @@ -170,7 +188,7 @@ enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_R enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; enum XsnsFunctions {FUNC_INIT, FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_SECOND, FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, - FUNC_COMMAND, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SHOW_SENSOR}; + FUNC_COMMAND, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SHOW_SENSOR, FUNC_RULES_PROCESS}; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index d8e6eb2f5..82e745b73 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -25,7 +25,7 @@ - Select IDE Tools - Flash Size: "1M (no SPIFFS)" ====================================================*/ -#define VERSION 0x050E0001 // 5.14.0a +#define VERSION 0x050E0002 // 5.14.0b // Location specific includes #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) @@ -97,6 +97,10 @@ const char kTasmotaCommands[] PROGMEM = D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER; // Global variables +unsigned long feature_drv1; // Compiled driver feature map +unsigned long feature_drv2; // Compiled driver feature map +unsigned long feature_sns1; // Compiled sensor feature map +unsigned long feature_sns2; // Compiled sensor feature map int baudrate = APP_BAUDRATE; // Serial interface baud rate SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit byte serial_in_byte; // Received byte @@ -176,7 +180,6 @@ uint8_t spi_flg = 0; // SPI configured uint8_t light_type = 0; // Light types bool pwm_present = false; // Any PWM channel configured with SetOption15 0 boolean mdns_begun = false; -unsigned long features = 0UL; uint8_t ntp_force_sync = 0; // Force NTP sync char my_version[33]; // Composed version string @@ -266,6 +269,9 @@ void GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) } fulltopic.replace(F(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]); fulltopic.replace(F(MQTT_TOKEN_TOPIC), topic); + String token_id = WiFi.macAddress(); + token_id.replace(":", ""); + fulltopic.replace(F(MQTT_TOKEN_ID), token_id); } fulltopic.replace(F("#"), ""); fulltopic.replace(F("//"), "/"); @@ -1171,11 +1177,9 @@ boolean SendKey(byte key, byte device, byte state) MqttPublishDirect(stopic, (key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain); #endif // USE_DOMOTICZ result = true; -#ifdef USE_RULES } else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); - result = RulesProcess(); -#endif // USE_RULES + result = XdrvRulesProcess(); } #ifdef USE_KNX KnxSendButtonPower(key, device, state); @@ -1294,11 +1298,11 @@ void ExecuteCommand(char *cmnd) void PublishStatus(uint8_t payload) { - uint8_t option = 1; + uint8_t option = STAT; char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +4)]; // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX - if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) option++; + if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) option++; // TELE if ((!Settings.flag.mqtt_enabled) && (6 == payload)) payload = 99; if (!energy_flg && (9 == payload)) payload = 99; @@ -1333,8 +1337,8 @@ void PublishStatus(uint8_t payload) } if ((0 == payload) || (4 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHMODE "\":%d}}"), - ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipMode()); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHMODE "\":%d,\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"), + ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipMode(), LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); } @@ -2162,6 +2166,7 @@ void SerialInput() serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); +// XdrvRulesProcess(); serial_in_byte_counter = 0; } } @@ -2372,6 +2377,8 @@ void setup() OsWatchInit(); + GetFeatures(); + baudrate = Settings.baudrate * 1200; seriallog_level = Settings.seriallog_level; seriallog_timer = SERIALLOG_TIMER; diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 7eb4ce225..f97c32203 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -95,6 +95,8 @@ enum UserSelectablePins { GPIO_SR04_ECHO, // SR04 Echo pin GPIO_SDM120_TX, // SDM120 Serial interface GPIO_SDM120_RX, // SDM120 Serial interface + GPIO_SDM630_TX, // SDM630 Serial interface + GPIO_SDM630_RX, // SDM630 Serial interface GPIO_SENSOR_END }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -139,7 +141,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_PMS5003 "|" D_SENSOR_SDS0X1 "|" D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX "|" D_SENSOR_SR04_TRIG "|" D_SENSOR_SR04_ECHO "|" - D_SENSOR_SDM120_TX "|" D_SENSOR_SDM120_RX; + D_SENSOR_SDM120_TX "|" D_SENSOR_SDM120_RX "|" + D_SENSOR_SDM630_TX "|" D_SENSOR_SDM630_RX; /********************************************************************************************/ diff --git a/sonoff/support.ino b/sonoff/support.ino index a12cbb6a1..2bc80c5eb 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -482,6 +482,96 @@ int GetStateNumber(char *state_text) return state_number; } +boolean GetUsedInModule(byte val, uint8_t *arr) +{ + int offset = 0; + + if (!val) { return false; } // None +#ifndef USE_I2C + if (GPIO_I2C_SCL == val) { return true; } + if (GPIO_I2C_SDA == val) { return true; } +#endif +#ifndef USE_SR04 + if (GPIO_SR04_TRIG == val) { return true; } + if (GPIO_SR04_ECHO == val) { return true; } +#endif +#ifndef USE_WS2812 + if (GPIO_WS2812 == val) { return true; } +#endif +#ifndef USE_IR_REMOTE + if (GPIO_IRSEND == val) { return true; } +#ifndef USE_IR_RECEIVE + if (GPIO_IRRECV == val) { return true; } +#endif +#endif +#ifndef USE_MHZ19 + if (GPIO_MHZ_TXD == val) { return true; } + if (GPIO_MHZ_RXD == val) { return true; } +#endif +#ifndef USE_PZEM004T + if (GPIO_PZEM_TX == val) { return true; } + if (GPIO_PZEM_RX == val) { return true; } +#endif +#ifndef USE_SENSEAIR + if (GPIO_SAIR_TX == val) { return true; } + if (GPIO_SAIR_RX == val) { return true; } +#endif +#ifndef USE_SPI + if (GPIO_SPI_CS == val) { return true; } + if (GPIO_SPI_DC == val) { return true; } +#endif +#ifndef USE_DISPLAY + if (GPIO_BACKLIGHT == val) { return true; } +#endif +#ifndef USE_PMS5003 + if (GPIO_PMS5003 == val) { return true; } +#endif +#ifndef USE_NOVA_SDS + if (GPIO_SDS0X1 == val) { return true; } +#endif +#ifndef USE_SERIAL_BRIDGE + if (GPIO_SBR_TX == val) { return true; } + if (GPIO_SBR_RX == val) { return true; } +#endif +#ifndef USE_SR04 + if (GPIO_SR04_TRIG == val) { return true; } + if (GPIO_SR04_ECHO == val) { return true; } +#endif +#ifndef USE_SDM120 + if (GPIO_SDM120_TX == val) { return true; } + if (GPIO_SDM120_RX == val) { return true; } +#endif +#ifndef USE_SDM630 + if (GPIO_SDM630_TX == val) { return true; } + if (GPIO_SDM630_RX == val) { return true; } +#endif + if ((val >= GPIO_REL1) && (val < GPIO_REL1 + MAX_RELAYS)) { + offset = (GPIO_REL1_INV - GPIO_REL1); + } + if ((val >= GPIO_REL1_INV) && (val < GPIO_REL1_INV + MAX_RELAYS)) { + offset = -(GPIO_REL1_INV - GPIO_REL1); + } + + if ((val >= GPIO_LED1) && (val < GPIO_LED1 + MAX_LEDS)) { + offset = (GPIO_LED1_INV - GPIO_LED1); + } + if ((val >= GPIO_LED1_INV) && (val < GPIO_LED1_INV + MAX_LEDS)) { + offset = -(GPIO_LED1_INV - GPIO_LED1); + } + + if ((val >= GPIO_PWM1) && (val < GPIO_PWM1 + MAX_PWMS)) { + offset = (GPIO_PWM1_INV - GPIO_PWM1); + } + if ((val >= GPIO_PWM1_INV) && (val < GPIO_PWM1_INV + MAX_PWMS)) { + offset = -(GPIO_PWM1_INV - GPIO_PWM1); + } + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if (arr[i] == val) { return true; } + if (arr[i] == val + offset) { return true; } + } + return false; +} + void SetSerialBaudrate(int baudrate) { Settings.baudrate = baudrate / 1200; @@ -516,6 +606,235 @@ uint32_t GetHash(const char *buffer, size_t size) return hash; } +/*********************************************************************************************\ + * Fill feature list +\*********************************************************************************************/ + +void GetFeatures() +{ + feature_drv1 = 0x00000000; // xdrv_00_mqtt.ino, xdrv_01_light.ino, xdrv_04_snfbridge.ino + +// feature_drv1 |= 0x00000001; +// feature_drv1 |= 0x00000002; + +#ifdef USE_I2C + feature_drv1 |= 0x00000004; // sonoff.ino +#endif +#ifdef USE_SPI + feature_drv1 |= 0x00000008; // sonoff.ino +#endif +#ifdef USE_DISCOVERY + feature_drv1 |= 0x00000010; // sonoff.ino +#endif +#ifdef USE_ARDUINO_OTA + feature_drv1 |= 0x00000020; // sonoff.ino +#endif +#ifdef USE_MQTT_TLS + feature_drv1 |= 0x00000040; // sonoff.ino +#endif +#ifdef USE_WEBSERVER + feature_drv1 |= 0x00000080; // webserver.ino +#endif +#ifdef WEBSERVER_ADVERTISE + feature_drv1 |= 0x00000100; // webserver.ino +#endif +#ifdef USE_EMULATION + feature_drv1 |= 0x00000200; // xplg_wemohue.ino +#endif +#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) + feature_drv1 |= 0x00000400; // xdrv_00_mqtt.ino +#endif +#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) + feature_drv1 |= 0x00000800; // xdrv_00_mqtt.ino +#endif +#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) + feature_drv1 |= 0x00001000; // xdrv_00_mqtt.ino +#endif +#ifdef MQTT_HOST_DISCOVERY + feature_drv1 |= 0x00002000; // xdrv_00_mqtt.ino +#endif +#ifdef USE_ARILUX_RF + feature_drv1 |= 0x00004000; // xdrv_01_light.ino +#endif +#ifdef USE_WS2812 + feature_drv1 |= 0x00008000; // xdrv_01_light.ino +#endif +#ifdef USE_WS2812_DMA + feature_drv1 |= 0x00010000; // xdrv_01_light.ino +#endif +#ifdef USE_IR_REMOTE + feature_drv1 |= 0x00020000; // xdrv_02_irremote.ino +#endif +#ifdef USE_IR_HVAC + feature_drv1 |= 0x00040000; // xdrv_02_irremote.ino +#endif +#ifdef USE_IR_RECEIVE + feature_drv1 |= 0x00080000; // xdrv_02_irremote.ino +#endif +#ifdef USE_DOMOTICZ + feature_drv1 |= 0x00100000; // xdrv_05_domoticz.ino +#endif +#ifdef USE_DISPLAY + feature_drv1 |= 0x00200000; // xdrv_06_display.ino +#endif +#ifdef USE_HOME_ASSISTANT + feature_drv1 |= 0x00400000; // xdrv_07_home_assistant.ino +#endif +#ifdef USE_SERIAL_BRIDGE + feature_drv1 |= 0x00800000; // xdrv_08_serial_bridge.ino +#endif +#ifdef USE_TIMERS + feature_drv1 |= 0x01000000; // xdrv_09_timers.ino +#endif +#ifdef USE_SUNRISE + feature_drv1 |= 0x02000000; // xdrv_09_timers.ino +#endif +#ifdef USE_TIMERS_WEB + feature_drv1 |= 0x04000000; // xdrv_09_timers.ino +#endif +#ifdef USE_RULES + feature_drv1 |= 0x08000000; // xdrv_10_rules.ino +#endif +#ifdef USE_KNX + feature_drv1 |= 0x10000000; // xdrv_11_knx.ino +#endif + +/*********************************************************************************************/ + + feature_drv2 = 0x00000000; + +#ifdef USE_CONFIG_OVERRIDE + feature_drv2 |= 0x00000001; // user_config(_override).h +#endif +#ifdef BE_MINIMAL + feature_drv2 |= 0x00000002; // user_config(_override).h +#endif +#ifdef USE_ALL_SENSORS + feature_drv2 |= 0x00000004; // user_config(_override).h +#endif +#ifdef USE_CLASSIC + feature_drv2 |= 0x00000008; // user_config(_override).h +#endif +#ifdef USE_KNX_NO_EMULATION + feature_drv2 |= 0x00000010; // user_config(_override).h +#endif + + +#ifdef VTABLES_IN_FLASH + feature_drv2 |= 0x04000000; // platformio.ini +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + feature_drv2 |= 0x08000000; // platformio.ini +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY + feature_drv2 |= 0x10000000; // platformio.ini +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH + feature_drv2 |= 0x20000000; // platformio.ini +#endif +#ifdef DEBUG_THEO + feature_drv2 |= 0x40000000; // xdrv_99_debug.ino +#endif +#ifdef USE_DEBUG_DRIVER + feature_drv2 |= 0x80000000; // xdrv_99_debug.ino +#endif + +/*********************************************************************************************/ + + feature_sns1 = 0x00000000; // xsns_01_counter.ino, xsns_04_snfsc.ino + +// feature_sns1 |= 0x00000001; + +#ifdef USE_ADC_VCC + feature_sns1 |= 0x00000002; // support.ino (ADC) +#endif +#ifdef USE_ENERGY_SENSOR + feature_sns1 |= 0x00000004; // xdrv_03_energy.ino +#endif +#ifdef USE_PZEM004T + feature_sns1 |= 0x00000008; // xdrv_03_energy.ino +#endif +#ifdef USE_DS18B20 + feature_sns1 |= 0x00000010; // xsns_05_ds18b20.ino +#endif +#ifdef USE_DS18x20_LEGACY + feature_sns1 |= 0x00000020; // xsns_05_ds18x20_legacy.ino +#endif +#ifdef USE_DS18x20 + feature_sns1 |= 0x00000040; // xsns_05_ds18x20.ino +#endif +#ifdef USE_DHT + feature_sns1 |= 0x00000080; // xsns_06_dht.ino +#endif +#ifdef USE_SHT + feature_sns1 |= 0x00000100; // xsns_07_sht1x.ino +#endif +#ifdef USE_HTU + feature_sns1 |= 0x00000200; // xsns_08_htu21.ino +#endif +#ifdef USE_BMP + feature_sns1 |= 0x00000400; // xsns_09_bmp.ino +#endif +#ifdef USE_BME680 + feature_sns1 |= 0x00000800; // xsns_09_bmp.ino - BME680 +#endif +#ifdef USE_BH1750 + feature_sns1 |= 0x00001000; // xsns_10_bh1750.ino +#endif +#ifdef USE_VEML6070 + feature_sns1 |= 0x00002000; // xsns_11_veml6070.ino +#endif +#ifdef USE_ADS1115_I2CDEV + feature_sns1 |= 0x00004000; // xsns_12_ads1115_i2cdev.ino +#endif +#ifdef USE_ADS1115 + feature_sns1 |= 0x00008000; // xsns_12_ads1115.ino +#endif +#ifdef USE_INA219 + feature_sns1 |= 0x00010000; // xsns_13_ina219.ino +#endif +#ifdef USE_SHT3X + feature_sns1 |= 0x00020000; // xsns_14_sht3x.ino +#endif +#ifdef USE_MHZ19 + feature_sns1 |= 0x00040000; // xsns_15_mhz19.ino +#endif +#ifdef USE_TSL2561 + feature_sns1 |= 0x00080000; // xsns_16_tsl2561.ino +#endif +#ifdef USE_SENSEAIR + feature_sns1 |= 0x00100000; // xsns_17_senseair.ino +#endif +#ifdef USE_PMS5003 + feature_sns1 |= 0x00200000; // xsns_18_pms5003.ino +#endif +#ifdef USE_MGS + feature_sns1 |= 0x00400000; // xsns_19_mgs.ino +#endif +#ifdef USE_NOVA_SDS + feature_sns1 |= 0x00800000; // xsns_20_novasds.ino +#endif +#ifdef USE_SGP30 + feature_sns1 |= 0x01000000; // xsns_21_sgp30.ino +#endif +#ifdef USE_SR04 + feature_sns1 |= 0x02000000; // xsns_22_sr04.ino +#endif +#ifdef USE_SDM120 + feature_sns1 |= 0x04000000; // xsns_23_sdm120.ino +#endif +#ifdef USE_SI1145 + feature_sns1 |= 0x08000000; // xsns_24_si1145.ino +#endif +#ifdef USE_SDM630 + feature_sns1 |= 0x10000000; // xsns_25_sdm630.ino +#endif + +/*********************************************************************************************/ + + feature_sns2 = 0x00000000; +} + /*********************************************************************************************\ * Wifi \*********************************************************************************************/ @@ -1381,14 +1700,12 @@ void RtcSecond() snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); AddLog(LOG_LEVEL_DEBUG); -#ifdef USE_RULES if (local_time < 1451602800) { // 2016-01-01 strncpy_P(mqtt_data, PSTR("{\"Time\":{\"Initialized\":1}}"), sizeof(mqtt_data)); } else { strncpy_P(mqtt_data, PSTR("{\"Time\":{\"Set\":1}}"), sizeof(mqtt_data)); } - RulesProcess(); -#endif // USE_RULES + XdrvRulesProcess(); } else { ntp_sync_minute++; // Try again in next minute } @@ -1469,7 +1786,7 @@ void AdcEvery50ms() adc_last_value = new_value; uint16_t value = adc_last_value / 10; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"ANALOG\":{\"A0div10\":%d}}"), (value > 99) ? 100 : value); - RulesProcess(); + XdrvRulesProcess(); } } } diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 2323e4a16..91b0a137f 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -268,7 +268,7 @@ #define USE_BH1750 // Add I2C code for BH1750 sensor (+0k5 code) // #define USE_VEML6070 // Add I2C code for VEML6070 sensor (+0k5 code) // #define USE_TSL2561 // Add I2C code for TSL2561 sensor using library Joba_Tsl2561 (+2k3 code) - #define USE_SI1145 // Add I2C code for SI1145/46/47 sensor (+1k code) +// #define USE_SI1145 // Add I2C code for SI1145/46/47 sensor (+1k code) // #define USE_ADS1115 // Add I2C code for ADS1115 16 bit A/D converter based on Adafruit ADS1x15 library (no library needed) (+0k7 code) // #define USE_ADS1115_I2CDEV // Add I2C code for ADS1115 16 bit A/D converter using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code) // #define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code) @@ -293,6 +293,8 @@ #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) #define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code) #define SDM120_SPEED 9600 // SDM120-Modbus RS485 serial speed (default: 2400 baud) +#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) + #define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud) // -- Low level interface devices ----------------- #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index 3c74d346a..e1656ec31 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -25,9 +25,6 @@ * Based on source by AlexT (https://github.com/tzapu) \*********************************************************************************************/ -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - const char HTTP_HEAD[] PROGMEM = "" "" @@ -331,7 +328,6 @@ uint8_t upload_progress_dot_count; uint8_t config_block_count = 0; uint8_t config_xor_on = 0; uint8_t config_xor_on_set = CONFIG_FILE_XOR; -uint8_t *settings_new = NULL; // Helper function to avoid code duplication (saves 4k Flash) static void WebGetArg(const char* arg, char* out, size_t max) @@ -642,52 +638,6 @@ void HandleConfiguration() ShowPage(page); } -boolean GetUsedInModule(byte val, uint8_t *arr) -{ - int offset = 0; - - if (!val) { return false; } // None -#ifndef USE_I2C - if (GPIO_I2C_SCL == val) { return true; } - if (GPIO_I2C_SDA == val) { return true; } -#endif -#ifndef USE_SR04 - if (GPIO_SR04_TRIG == val) { return true; } - if (GPIO_SR04_ECHO == val) { return true; } -#endif -#ifndef USE_WS2812 - if (GPIO_WS2812 == val) { return true; } -#endif -#ifndef USE_IR_REMOTE - if (GPIO_IRSEND == val) { return true; } -#endif - if ((val >= GPIO_REL1) && (val < GPIO_REL1 + MAX_RELAYS)) { - offset = (GPIO_REL1_INV - GPIO_REL1); - } - if ((val >= GPIO_REL1_INV) && (val < GPIO_REL1_INV + MAX_RELAYS)) { - offset = -(GPIO_REL1_INV - GPIO_REL1); - } - - if ((val >= GPIO_LED1) && (val < GPIO_LED1 + MAX_LEDS)) { - offset = (GPIO_LED1_INV - GPIO_LED1); - } - if ((val >= GPIO_LED1_INV) && (val < GPIO_LED1_INV + MAX_LEDS)) { - offset = -(GPIO_LED1_INV - GPIO_LED1); - } - - if ((val >= GPIO_PWM1) && (val < GPIO_PWM1 + MAX_PWMS)) { - offset = (GPIO_PWM1_INV - GPIO_PWM1); - } - if ((val >= GPIO_PWM1_INV) && (val < GPIO_PWM1_INV + MAX_PWMS)) { - offset = -(GPIO_PWM1_INV - GPIO_PWM1); - } - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - if (arr[i] == val) { return true; } - if (arr[i] == val + offset) { return true; } - } - return false; -} - void HandleModuleConfiguration() { if (HttpUser()) { return; } @@ -964,25 +914,36 @@ void HandleBackupConfiguration() if (HttpUser()) { return; } AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); - uint8_t buffer[sizeof(Settings)]; + if (!SettingsBufferAlloc()) { return; } WiFiClient myClient = WebServer->client(); - WebServer->setContentLength(sizeof(buffer)); + WebServer->setContentLength(sizeof(Settings)); char attachment[100]; char friendlyname[sizeof(Settings.friendlyname[0])]; snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, Settings.friendlyname[0]), my_version); WebServer->sendHeader(F("Content-Disposition"), attachment); + WebServer->send(200, FPSTR(HDR_CTYPE_STREAM), ""); - memcpy(buffer, &Settings, sizeof(buffer)); - buffer[0] = CONFIG_FILE_SIGN; - buffer[1] = (!config_xor_on_set) ? 0 : 1; - if (buffer[1]) { - for (uint16_t i = 2; i < sizeof(buffer); i++) { - buffer[i] ^= (config_xor_on_set +i); + memcpy(settings_buffer, &Settings, sizeof(Settings)); + settings_buffer[0] = CONFIG_FILE_SIGN; + settings_buffer[1] = (!config_xor_on_set) ? 0 : 1; + if (settings_buffer[1]) { + for (uint16_t i = 2; i < sizeof(Settings); i++) { + settings_buffer[i] ^= (config_xor_on_set +i); } } - myClient.write((const char*)buffer, sizeof(buffer)); + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + size_t written = myClient.write((const char*)settings_buffer, sizeof(Settings)); + if (written < sizeof(Settings)) { // https://github.com/esp8266/Arduino/issues/3218 + myClient.write((const char*)settings_buffer +written, sizeof(Settings) -written); + } +#else + myClient.write((const char*)settings_buffer, sizeof(Settings)); +#endif + + SettingsBufferFree(); } void HandleSaveSettings() @@ -1235,14 +1196,6 @@ void HandleUpgradeFirmwareStart() ExecuteCommand(svalue); } -void SettingsNewFree() -{ - if (settings_new != NULL) { - free(settings_new); - settings_new = NULL; - } -} - void HandleUploadDone() { if (HttpUser()) { return; } @@ -1282,7 +1235,7 @@ void HandleUploadDone() page += FPSTR(HTTP_MSG_RSTRT); restart_flag = 2; } - SettingsNewFree(); + SettingsBufferFree(); page += F("
"); page += FPSTR(HTTP_BTN_MAIN); ShowPage(page); @@ -1311,8 +1264,7 @@ void HandleUploadLoop() snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); AddLog(LOG_LEVEL_INFO); if (upload_file_type) { - SettingsNewFree(); - if (!(settings_new = (uint8_t *)malloc(sizeof(Settings)))) { + if (!SettingsBufferAlloc()) { upload_error = 2; return; } @@ -1360,7 +1312,7 @@ void HandleUploadLoop() upload_error = 9; return; } - memcpy(settings_new + (config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); + memcpy(settings_buffer + (config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); config_block_count++; } } else { // firmware @@ -1381,13 +1333,13 @@ void HandleUploadLoop() if (upload_file_type) { if (config_xor_on) { for (uint16_t i = 2; i < sizeof(Settings); i++) { - settings_new[i] ^= (config_xor_on_set +i); + settings_buffer[i] ^= (config_xor_on_set +i); } } SettingsDefaultSet2(); - memcpy((char*)&Settings +16, settings_new +16, sizeof(Settings) -16); - memcpy((char*)&Settings +8, settings_new +8, 4); // Restore version and auto upgrade - SettingsNewFree(); + memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16); + memcpy((char*)&Settings +8, settings_buffer +8, 4); // Restore version and auto upgrade + SettingsBufferFree(); } else { if (!Update.end(true)) { // true to set the size to the current progress if (_serialoutput) { Update.printError(Serial); } diff --git a/sonoff/xdrv_00_mqtt.ino b/sonoff/xdrv_00_mqtt.ino index c3eda47d2..5d688e721 100644 --- a/sonoff/xdrv_00_mqtt.ino +++ b/sonoff/xdrv_00_mqtt.ino @@ -315,10 +315,8 @@ void MqttDisconnected(int state) snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, state, mqtt_retry_counter); AddLog(LOG_LEVEL_INFO); -#ifdef USE_RULES strncpy_P(mqtt_data, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(mqtt_data)); - RulesProcess(); -#endif // USE_RULES + XdrvRulesProcess(); } void MqttConnected() @@ -370,17 +368,13 @@ void MqttConnected() tele_period = Settings.tele_period -9; } status_update_timer = 2; -#ifdef USE_RULES strncpy_P(mqtt_data, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(mqtt_data)); - RulesProcess(); -#endif // USE_RULES + XdrvRulesProcess(); XdrvCall(FUNC_MQTT_INIT); } mqtt_initial_connection_state = 0; -#ifdef USE_RULES strncpy_P(mqtt_data, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(mqtt_data)); - RulesProcess(); -#endif // USE_RULES + XdrvRulesProcess(); } #ifdef USE_MQTT_TLS diff --git a/sonoff/xdrv_02_irremote.ino b/sonoff/xdrv_02_irremote.ino index 99f4dfed7..eed1fd228 100644 --- a/sonoff/xdrv_02_irremote.ino +++ b/sonoff/xdrv_02_irremote.ino @@ -110,6 +110,7 @@ void IrReceiveCheck() snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":\"%lX\"}}"), GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, (uint32_t)results.value); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + XdrvRulesProcess(); #ifdef USE_DOMOTICZ unsigned long value = results.value | (iridx << 28); // [Protocol:4, Data:28] DomoticzSensor(DZ_COUNT, value); // Send data as Domoticz Counter value @@ -298,7 +299,7 @@ boolean IrSendCommand() protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], NULL, 0); - if (protocol && bits && data) { + if (protocol && bits) { int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); snprintf_P(log_data, sizeof(log_data), PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %u (0x%lX), protocol_code %d"), @@ -384,7 +385,7 @@ boolean IrSendCommand() } #endif // USE_IR_HVAC else serviced = false; // Unknown command - + return serviced; } diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index c41af56a4..b3d717d65 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -1113,6 +1113,17 @@ void EnergyShow(boolean json) DomoticzSensor(DZ_CURRENT, energy_current_chr); // Current } #endif // USE_DOMOTICZ +#ifdef USE_KNX + if (show_energy_period) { + KnxSensor(KNX_ENERGY_VOLTAGE, energy_voltage); + KnxSensor(KNX_ENERGY_CURRENT, energy_current); + KnxSensor(KNX_ENERGY_POWER, energy_power); + KnxSensor(KNX_ENERGY_POWERFACTOR, energy_power_factor); + KnxSensor(KNX_ENERGY_DAILY, energy_daily); + KnxSensor(KNX_ENERGY_TOTAL, energy_total); + KnxSensor(KNX_ENERGY_START, energy_start); + } +#endif // USE_KNX #ifdef USE_WEBSERVER } else { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS, mqtt_data, energy_voltage_chr, energy_current_chr, energy_power_chr, energy_power_factor_chr, energy_daily_chr, energy_yesterday_chr, energy_total_chr); diff --git a/sonoff/xdrv_04_snfbridge.ino b/sonoff/xdrv_04_snfbridge.ino index 20c9fa3d1..8aa300f5d 100644 --- a/sonoff/xdrv_04_snfbridge.ino +++ b/sonoff/xdrv_04_snfbridge.ino @@ -97,6 +97,7 @@ void SonoffBridgeReceived() snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\",\"" D_CMND_RFKEY "\":%s}}"), sync_time, low_time, high_time, received_id, rfkey); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); + XdrvRulesProcess(); #ifdef USE_DOMOTICZ DomoticzSensor(DZ_COUNT, received_id); // Send rid as Domoticz Counter value #endif // USE_DOMOTICZ diff --git a/sonoff/xdrv_08_serial_bridge.ino b/sonoff/xdrv_08_serial_bridge.ino index 455179f98..653649b91 100644 --- a/sonoff/xdrv_08_serial_bridge.ino +++ b/sonoff/xdrv_08_serial_bridge.ino @@ -61,6 +61,7 @@ void SerialBridgeInput() serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; // serial data completed snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), serial_bridge_buffer); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); +// XdrvRulesProcess(); serial_bridge_in_byte_counter = 0; } } diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 889a92937..bc517ae1a 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -262,7 +262,7 @@ void TimerEverySecond() { if (RtcTime.valid) { if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } // Midnight - if (RtcTime.minute != timer_last_minute) { // Execute every minute only once + if ((uptime > 60) && (RtcTime.minute != timer_last_minute)) { // Execute from one minute after restart every minute only once timer_last_minute = RtcTime.minute; int16_t time = (RtcTime.hour *60) + RtcTime.minute; uint8_t days = 1 << (RtcTime.day_of_week -1); @@ -287,7 +287,7 @@ void TimerEverySecond() #ifdef USE_RULES if (3 == xtimer.power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); - RulesProcess(); + XdrvRulesProcess(); } else #endif // USE_RULES if (devices_present) { ExecuteCommandPower(xtimer.device +1, xtimer.power); } diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 076e7333d..abaf700a2 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -72,11 +72,13 @@ #define D_CMND_RULE "Rule" #define D_CMND_RULETIMER "RuleTimer" #define D_CMND_EVENT "Event" +#define D_CMND_VAR "Var" +#define D_CMND_MEM "Mem" #define D_JSON_INITIATED "Initiated" -enum RulesCommands { CMND_RULE, CMND_RULETIMER, CMND_EVENT }; -const char kRulesCommands[] PROGMEM = D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT ; +enum RulesCommands { CMND_RULE, CMND_RULETIMER, CMND_EVENT, CMND_VAR, CMND_MEM }; +const char kRulesCommands[] PROGMEM = D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM ; String rules_event_value; unsigned long rules_timer[MAX_RULE_TIMERS] = { 0 }; @@ -84,10 +86,12 @@ uint8_t rules_quota = 0; long rules_new_power = -1; long rules_old_power = -1; -uint32_t rules_triggers = 0; -uint8_t rules_trigger_count = 0; +uint32_t rules_triggers[MAX_RULE_SETS] = { 0 }; +uint8_t rules_trigger_count[MAX_RULE_SETS] = { 0 }; uint8_t rules_teleperiod = 0; +char vars[RULES_MAX_VARS][10] = { 0 }; + /*******************************************************************************************/ long TimeDifference(unsigned long prev, unsigned long next) @@ -137,13 +141,14 @@ bool TimeReached(unsigned long timer) /*******************************************************************************************/ -bool RulesRuleMatch(String &event, String &rule) +bool RulesRuleMatch(byte rule_set, String &event, String &rule) { // event = {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}} // event = {"System":{"Boot":1}} // rule = "INA219#CURRENT>0.100" bool match = false; + char stemp[10]; // Step1: Analyse rule int pos = rule.indexOf('#'); @@ -174,15 +179,32 @@ bool RulesRuleMatch(String &event, String &rule) } } - char tmp_value[CMDSZ] = { 0 }; + char rule_svalue[CMDSZ] = { 0 }; double rule_value = 0; if (pos > 0) { - snprintf(tmp_value, sizeof(tmp_value), rule_name.substring(pos + 1).c_str()); - int temp_value = GetStateNumber(tmp_value); + String rule_param = rule_name.substring(pos + 1); + for (byte i = 0; i < RULES_MAX_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = vars[i]; + break; + } + } + for (byte i = 0; i < RULES_MAX_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = Settings.mems[i]; + break; + } + } + rule_param.toUpperCase(); + snprintf(rule_svalue, sizeof(rule_svalue), rule_param.c_str()); + + int temp_value = GetStateNumber(rule_svalue); if (temp_value > -1) { rule_value = temp_value; } else { - rule_value = CharToDouble((char*)tmp_value); // 0.1 - This saves 9k code over toFLoat()! + rule_value = CharToDouble((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()! } rule_name = rule_name.substring(0, pos); // "CURRENT" } @@ -196,7 +218,7 @@ bool RulesRuleMatch(String &event, String &rule) const char* str_value = root[rule_task][rule_name]; //snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), -// rule_task.c_str(), rule_name.c_str(), tmp_value, rules_trigger_count, bitRead(rules_triggers, rules_trigger_count), event.c_str(), (str_value) ? str_value : "none"); +// rule_task.c_str(), rule_name.c_str(), rule_svalue, rules_trigger_count[rule_set], bitRead(rules_triggers[rule_set], rules_trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none"); //AddLog(LOG_LEVEL_DEBUG); if (!root[rule_task][rule_name].success()) { return false; } @@ -209,13 +231,14 @@ bool RulesRuleMatch(String &event, String &rule) value = CharToDouble((char*)str_value); switch (compare) { case '>': - if (value > rule_value) match = true; + if (value > rule_value) { match = true; } break; case '<': - if (value < rule_value) match = true; + if (value < rule_value) { match = true; } break; case '=': - if (value == rule_value) match = true; +// if (value == rule_value) { match = true; } // Compare values - only decimals or partly hexadecimals + if (!strcasecmp(str_value, rule_svalue)) { match = true; } // Compare strings - this also works for hexadecimals break; case ' ': match = true; // Json value but not needed @@ -225,13 +248,13 @@ bool RulesRuleMatch(String &event, String &rule) if (Settings.flag.rules_once) { if (match) { // Only allow match state changes - if (!bitRead(rules_triggers, rules_trigger_count)) { - bitSet(rules_triggers, rules_trigger_count); + if (!bitRead(rules_triggers[rule_set], rules_trigger_count[rule_set])) { + bitSet(rules_triggers[rule_set], rules_trigger_count[rule_set]); } else { match = false; } } else { - bitClear(rules_triggers, rules_trigger_count); + bitClear(rules_triggers[rule_set], rules_trigger_count[rule_set]); } } @@ -240,25 +263,19 @@ bool RulesRuleMatch(String &event, String &rule) /*******************************************************************************************/ -bool RulesProcess() +bool RuleSetProcess(byte rule_set, String &event_saved) { bool serviced = false; - char vars[RULES_MAX_VARS][10] = { 0 }; char stemp[10]; delay(0); // Prohibit possible loop software watchdog -//snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Event = %s, Rule = %s"), mqtt_data, Settings.rules); +//snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]); //AddLog(LOG_LEVEL_DEBUG); - if (!Settings.flag.rules_enabled) { return serviced; } // Not enabled - if (!strlen(Settings.rules)) { return serviced; } // No rules + String rules = Settings.rules[rule_set]; - String event_saved = mqtt_data; - event_saved.toUpperCase(); - String rules = Settings.rules; - - rules_trigger_count = 0; + rules_trigger_count[rule_set] = 0; int plen = 0; while (true) { rules = rules.substring(plen); // Select relative to last rule @@ -283,54 +300,69 @@ bool RulesProcess() //snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Event |%s|, Rule |%s|, Command(s) |%s|"), event.c_str(), event_trigger.c_str(), commands.c_str()); //AddLog(LOG_LEVEL_DEBUG); - if (RulesRuleMatch(event, event_trigger)) { + if (RulesRuleMatch(rule_set, event, event_trigger)) { commands.trim(); String ucommand = commands; ucommand.toUpperCase(); - if (ucommand.startsWith("VAR")) { - uint8_t idx = ucommand.charAt(3) - '1'; - if ((idx >= 0) && (idx < RULES_MAX_VARS)) { snprintf(vars[idx], sizeof(vars[idx]), rules_event_value.c_str()); } - } else { -// if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race condition - commands.replace(F("%value%"), rules_event_value); - for (byte i = 0; i < RULES_MAX_VARS; i++) { - if (strlen(vars[i])) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%var%d%%"), i +1); - commands.replace(stemp, vars[i]); - } - } - char command[commands.length() +1]; - snprintf(command, sizeof(command), commands.c_str()); - - snprintf_P(log_data, sizeof(log_data), PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); - AddLog(LOG_LEVEL_INFO); - -// snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, D_CMND_RULE, D_JSON_INITIATED); -// MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RULE)); - - ExecuteCommand(command); +// if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception + if (ucommand.indexOf("EVENT ") != -1) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception + commands.replace(F("%value%"), rules_event_value); + for (byte i = 0; i < RULES_MAX_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%var%d%%"), i +1); + commands.replace(stemp, vars[i]); } + for (byte i = 0; i < RULES_MAX_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%mem%d%%"), i +1); + commands.replace(stemp, Settings.mems[i]); + } + char command[commands.length() +1]; + snprintf(command, sizeof(command), commands.c_str()); + + snprintf_P(log_data, sizeof(log_data), PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); + AddLog(LOG_LEVEL_INFO); + +// snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, D_CMND_RULE, D_JSON_INITIATED); +// MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RULE)); + + ExecuteCommand(command); serviced = true; } - rules_trigger_count++; + rules_trigger_count[rule_set]++; } return serviced; } /*******************************************************************************************/ +bool RulesProcess() +{ + bool serviced = false; + + String event_saved = mqtt_data; + event_saved.toUpperCase(); + + for (byte i = 0; i < MAX_RULE_SETS; i++) { + if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) { + if (RuleSetProcess(i, event_saved)) { serviced = true; } + } + } + return serviced; +} + void RulesInit() { - if (Settings.rules[0] == '\0') { - Settings.flag.rules_enabled = 0; - Settings.flag.rules_once = 0; + for (byte i = 0; i < MAX_RULE_SETS; i++) { + if (Settings.rules[i][0] == '\0') { + bitWrite(Settings.rule_enabled, i, 0); + bitWrite(Settings.rule_once, i, 0); + } } rules_teleperiod = 0; } void RulesEvery50ms() { - if (Settings.flag.rules_enabled) { + if (Settings.rule_enabled) { // Any rule enabled if (rules_new_power != rules_old_power) { if (rules_old_power != -1) { for (byte i = 0; i < devices_present; i++) { @@ -362,7 +394,7 @@ void RulesEvery50ms() void RulesEverySecond() { - if (Settings.flag.rules_enabled) { + if (Settings.rule_enabled) { // Any rule enabled for (byte i = 0; i < MAX_RULE_TIMERS; i++) { if (rules_timer[i] != 0L) { // Timer active? if (TimeReached(rules_timer[i])) { // Timer finished? @@ -397,39 +429,32 @@ boolean RulesCommand() if (-1 == command_code) { serviced = false; // Unknown command } - else if (CMND_RULE == command_code) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules))) { + else if ((CMND_RULE == command_code) && (index > 0) && (index <= MAX_RULE_SETS)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 6)) { switch (XdrvMailbox.payload) { case 0: // Off case 1: // On - Settings.flag.rules_enabled = XdrvMailbox.payload; + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); break; case 2: // Toggle - Settings.flag.rules_enabled ^= 1; + bitWrite(Settings.rule_enabled, index -1, bitRead(Settings.rule_enabled, index -1) ^1); break; case 4: // Off case 5: // On - Settings.flag.rules_once = XdrvMailbox.payload &1; + bitWrite(Settings.rule_once, index -1, XdrvMailbox.payload &1); break; case 6: // Toggle - Settings.flag.rules_once ^= 1; + bitWrite(Settings.rule_once, index -1, bitRead(Settings.rule_once, index -1) ^1); break; } } else { -/* - String uc_data = XdrvMailbox.data; // Do not allow Rule to be used within a rule - uc_data.toUpperCase(); - String uc_command = command; - uc_command += " "; // Distuingish from RuleTimer - uc_command.toUpperCase(); - if (!uc_data.indexOf(uc_command)) { strlcpy(Settings.rules, XdrvMailbox.data, sizeof(Settings.rules)); } -*/ - strlcpy(Settings.rules, XdrvMailbox.data, sizeof(Settings.rules)); + strlcpy(Settings.rules[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1])); } - rules_triggers = 0; // Reset once flag + rules_triggers[index -1] = 0; // Reset once flag } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\",\"Once\":\"%s\",\"Rules\":\"%s\"}"), command, GetStateText(Settings.flag.rules_enabled), GetStateText(Settings.flag.rules_once), Settings.rules); + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), + command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); } else if ((CMND_RULETIMER == command_code) && (index > 0) && (index <= MAX_RULE_TIMERS)) { if (XdrvMailbox.data_len > 0) { @@ -453,6 +478,18 @@ boolean RulesCommand() } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } + else if ((CMND_VAR == command_code) && (index > 0) && (index <= RULES_MAX_VARS)) { + if (XdrvMailbox.data_len > 0) { + strlcpy(vars[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(vars[index -1])); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); + } + else if ((CMND_MEM == command_code) && (index > 0) && (index <= RULES_MAX_MEMS)) { + if (XdrvMailbox.data_len > 0) { + strlcpy(Settings.mems[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[index -1])); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mems[index -1]); + } else serviced = false; // Unknown command return serviced; @@ -484,8 +521,11 @@ boolean Xdrv10(byte function) case FUNC_COMMAND: result = RulesCommand(); break; + case FUNC_RULES_PROCESS: + result = RulesProcess(); + break; } return result; } -#endif // USE_RULES \ No newline at end of file +#endif // USE_RULES diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 89959d269..dfbdbb33b 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -48,21 +48,26 @@ byte Settings.knx_CB_param[MAX_KNX_CB] Type of Output (set relay, t \*********************************************************************************************/ -#include +#include // KNX Library + // Note: Inside the file there is a //#define USE_ASYNC_UDP // UDP WIFI Library Selection for Multicast + // If commented out, the esp-knx-ip library will use WIFI_UDP Library that is compatible with ESP8266 Library Version 2.3.0 and up + // If not commented out, the esp-knx-ip library will use ESPAsyncUDP Library that is compatible with ESP8266 Library Version 2.4.0 and up + // The ESPAsyncUDP Library have a more reliable multicast communication + // Please Use it with Patch (https://github.com/me-no-dev/ESPAsyncUDP/pull/21) ) -//void KNX_CB_Action(message_t const &msg, void *arg); // Define function (action callback) to be called by the KNX_IP Library +//void KNX_CB_Action(message_t const &msg, void *arg); // Define function (action callback) to be called by the Esp-KNX-IP Library // when an action is requested by another KNX Device address_t KNX_physs_addr; // Physical KNX address of this device address_t KNX_addr; // KNX Address converter variable #define KNX_Empty 255 -#define KNX_TEMPERATURE 17 -#define KNX_HUMIDITY 18 -#define KNX_MAX_device_param 18 + +#define TOGGLE_INHIBIT_TIME 15 // 15*50mseg = 750mseg (inhibit time for not toggling again relays by a KNX toggle command) float last_temp; float last_hum; +byte toggle_inhibit; typedef struct __device_parameters { @@ -103,6 +108,13 @@ device_parameters_t device_param[] = { { 16, false, false, KNX_Empty }, // device_param[15] = Button 8 { KNX_TEMPERATURE, false, false, KNX_Empty }, // device_param[16] = Temperature { KNX_HUMIDITY , false, false, KNX_Empty }, // device_param[17] = humidity + { KNX_ENERGY_VOLTAGE , false, false, KNX_Empty }, + { KNX_ENERGY_CURRENT , false, false, KNX_Empty }, + { KNX_ENERGY_POWER , false, false, KNX_Empty }, + { KNX_ENERGY_POWERFACTOR , false, false, KNX_Empty }, + { KNX_ENERGY_DAILY , false, false, KNX_Empty }, + { KNX_ENERGY_START , false, false, KNX_Empty }, + { KNX_ENERGY_TOTAL , false, false, KNX_Empty }, { KNX_Empty, false, false, KNX_Empty} }; @@ -126,6 +138,13 @@ const char * device_param_ga[] = { D_SENSOR_BUTTON " 8", // Button 8 D_TEMPERATURE , // Temperature D_HUMIDITY , // Humidity + D_VOLTAGE , + D_CURRENT , + D_POWERUSAGE , + D_POWER_FACTOR , + D_ENERGY_TODAY , + D_ENERGY_YESTERDAY , + D_ENERGY_TOTAL , nullptr }; @@ -149,6 +168,13 @@ const char *device_param_cb[] = { D_TIMER_OUTPUT " 8 " D_BUTTON_TOGGLE, D_REPLY " " D_TEMPERATURE, // Reply Temperature D_REPLY " " D_HUMIDITY, // Reply Humidity + D_REPLY " " D_VOLTAGE , + D_REPLY " " D_CURRENT , + D_REPLY " " D_POWERUSAGE , + D_REPLY " " D_POWER_FACTOR , + D_REPLY " " D_ENERGY_TODAY , + D_REPLY " " D_ENERGY_YESTERDAY , + D_REPLY " " D_ENERGY_TOTAL , nullptr }; @@ -411,6 +437,17 @@ void KNX_INIT() if (GetUsedInModule(GPIO_DHT22, my_module.gp.io)) { device_param[KNX_HUMIDITY-1].show = true; } if (GetUsedInModule(GPIO_SI7021, my_module.gp.io)) { device_param[KNX_HUMIDITY-1].show = true; } + // Sonoff 31 or Sonoff Pow or any HLW8012 based device or Sonoff POW R2 or Any device with a Pzem004T + if ( ( SONOFF_S31 == Settings.module ) || ( SONOFF_POW_R2 == Settings.module ) || ( energy_flg != ENERGY_NONE ) ) { + device_param[KNX_ENERGY_POWER-1].show = true; + device_param[KNX_ENERGY_DAILY-1].show = true; + device_param[KNX_ENERGY_START-1].show = true; + device_param[KNX_ENERGY_TOTAL-1].show = true; + device_param[KNX_ENERGY_VOLTAGE-1].show = true; + device_param[KNX_ENERGY_CURRENT-1].show = true; + device_param[KNX_ENERGY_POWERFACTOR-1].show = true; + } + // Delete from KNX settings all configuration is not anymore related to this device if (KNX_CONFIG_NOT_MATCH()) { Settings.knx_GA_registered = 0; @@ -461,21 +498,38 @@ void KNX_CB_Action(message_t const &msg, void *arg) } else if (chan->type < 17) // Toggle Relays { - ExecuteCommandPower((chan->type) -8, 2); + if (!toggle_inhibit) { + ExecuteCommandPower((chan->type) -8, 2); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } } break; case KNX_CT_READ: if (chan->type < 9) // reply Relays status { knx.answer_1bit(msg.received_on, chan->last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_1bit(msg.received_on, chan->last_state); + knx.answer_1bit(msg.received_on, chan->last_state); + } } else if (chan->type = KNX_TEMPERATURE) // Reply Temperature { knx.answer_2byte_float(msg.received_on, last_temp); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_temp); + knx.answer_2byte_float(msg.received_on, last_temp); + } } else if (chan->type = KNX_HUMIDITY) // Reply Humidity { knx.answer_2byte_float(msg.received_on, last_hum); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_hum); + knx.answer_2byte_float(msg.received_on, last_hum); + } } break; } @@ -493,6 +547,10 @@ void KnxUpdatePowerState(byte device, power_t state) while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_1bit(KNX_addr, device_param[device -1].last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + } snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), device_param_ga[device -1], device_param[device -1].last_state, @@ -522,6 +580,10 @@ void KnxSendButtonPower(byte key, byte device, byte state) while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_1bit(KNX_addr, !(state == 0)); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, !(state == 0)); + knx.write_1bit(KNX_addr, !(state == 0)); + } snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), device_param_ga[device + 7], !(state == 0), @@ -550,6 +612,10 @@ void KnxSensor(byte sensor_type, float value) while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_2byte_float(KNX_addr, value); + if (Settings.flag.knx_enable_enhancement) { + knx.write_2byte_float(KNX_addr, value); + knx.write_2byte_float(KNX_addr, value); + } snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), device_param_ga[sensor_type -1], @@ -578,8 +644,11 @@ const char HTTP_FORM_KNX[] PROGMEM = "

" D_KNX_PHYSICAL_ADDRESS_NOTE "

" "" D_KNX_ENABLE " " D_KNX_ENABLE "

" + ">" D_KNX_ENHANCEMENT "

" "
" "" D_KNX_GROUP_ADDRESS_TO_WRITE "
" @@ -696,6 +765,8 @@ void HandleKNXConfiguration() page.replace(F("{knl"), String(KNX_physs_addr.pa.line)); page.replace(F("{knm"), String(KNX_physs_addr.pa.member)); if ( Settings.flag.knx_enabled ) { page += F(" checked"); } + page += FPSTR(HTTP_FORM_KNX1); + if ( Settings.flag.knx_enable_enhancement ) { page += F(" checked"); } page += FPSTR(HTTP_FORM_KNX2); for (byte i = 0; i < KNX_MAX_device_param ; i++) @@ -822,8 +893,9 @@ void KNX_Save_Settings() address_t KNX_addr; Settings.flag.knx_enabled = WebServer->hasArg("b1"); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_ENABLED ": %d "), - Settings.flag.knx_enabled); + Settings.flag.knx_enable_enhancement = WebServer->hasArg("b2"); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), + Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement ); AddLog(LOG_LEVEL_DEBUG); stmp = WebServer->arg("area"); @@ -883,6 +955,11 @@ boolean Xdrv11(byte function) case FUNC_LOOP: knx.loop(); // Process knx events break; + case FUNC_EVERY_50_MSECOND: + if (toggle_inhibit) { + toggle_inhibit--; + } + break; // case FUNC_COMMAND: // result = KNXCommand(); // break; diff --git a/sonoff/xdrv_interface.ino b/sonoff/xdrv_interface.ino index 09f056ece..d075f3136 100644 --- a/sonoff/xdrv_interface.ino +++ b/sonoff/xdrv_interface.ino @@ -175,6 +175,11 @@ boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t return XdrvCall(FUNC_MQTT_DATA); } +boolean XdrvRulesProcess() +{ + return XdrvCall(FUNC_RULES_PROCESS); +} + /*********************************************************************************************\ * Function call to all xdrv * @@ -188,6 +193,7 @@ boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t * FUNC_SHOW_SENSOR * FUNC_EVERY_SECOND * FUNC_EVERY_50_MSECOND + * FUNC_RULES_PROCESS \*********************************************************************************************/ boolean XdrvCall(byte Function) diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 965fd7483..aba8827b0 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -489,7 +489,7 @@ const char HueConfigResponse_JSON[] PROGMEM = "\"last use date\":\"{dt\"," "\"create date\":\"{dt\"," "\"name\":\"Remote\"}}," - "\"swversion\":\"01039019\"," + "\"swversion\":\"01041302\"," "\"apiversion\":\"1.17.0\"," "\"swupdate\":{\"updatestate\":0,\"url\":\"\",\"text\":\"\",\"notify\": false}," "\"linkbutton\":false," diff --git a/sonoff/xsns_25_sdm630.ino b/sonoff/xsns_25_sdm630.ino new file mode 100644 index 000000000..40dc8d0f9 --- /dev/null +++ b/sonoff/xsns_25_sdm630.ino @@ -0,0 +1,355 @@ +/* + xsns_25_sdm630.ino - Eastron SDM630-Modbus energy meter support for Sonoff-Tasmota + + Copyright (C) 2018 Gennaro Tortone + + 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 . +*/ + +#ifdef USE_SDM630 + +/*********************************************************************************************\ + * Eastron SDM630-Modbus energy meter + * + * Based on: https://github.com/reaper7/SDM_Energy_Meter +\*********************************************************************************************/ + +#include + +TasmotaSerial *SDM630Serial; + +uint8_t sdm630_type = 1; +uint8_t sdm630_state = 0; + +float sdm630_voltage[] = {0,0,0}; +float sdm630_current[] = {0,0,0}; +float sdm630_active_power[] = {0,0,0}; +float sdm630_reactive_power[] = {0,0,0}; +float sdm630_power_factor[] = {0,0,0}; +float sdm630_energy_total = 0; + +bool SDM630_ModbusReceiveReady() +{ + return (SDM630Serial->available() > 1); +} + +void SDM630_ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t register_count) +{ + uint8_t frame[8]; + + frame[0] = 0x01; // default SDM630 Modbus address + frame[1] = function_code; + frame[2] = (uint8_t)(start_address >> 8); + frame[3] = (uint8_t)(start_address); + frame[4] = (uint8_t)(register_count >> 8); + frame[5] = (uint8_t)(register_count); + + uint16_t crc = SDM630_calculateCRC(frame, 6); // calculate out crc only from first 6 bytes + frame[6] = lowByte(crc); + frame[7] = highByte(crc); + + while (SDM630Serial->available() > 0) { // read serial if any old data is available + SDM630Serial->read(); + } + + SDM630Serial->flush(); + SDM630Serial->write(frame, sizeof(frame)); +} + +uint8_t SDM630_ModbusReceive(float *value) +{ + uint8_t buffer[9]; + + *value = NAN; + uint8_t len = 0; + while (SDM630Serial->available() > 0) { + buffer[len++] = (uint8_t)SDM630Serial->read(); + } + + if (len < 9) + return 3; // SDM_ERR_NOT_ENOUGHT_BYTES + + if (len == 9) { + + if (buffer[0] == 0x01 && buffer[1] == 0x04 && buffer[2] == 4) { // check node number, op code and reply bytes count + + if((SDM630_calculateCRC(buffer, 7)) == ((buffer[8] << 8) | buffer[7])) { //calculate crc from first 7 bytes and compare with received crc (bytes 7 & 8) + + ((uint8_t*)value)[3] = buffer[3]; + ((uint8_t*)value)[2] = buffer[4]; + ((uint8_t*)value)[1] = buffer[5]; + ((uint8_t*)value)[0] = buffer[6]; + + } else return 1; // SDM_ERR_CRC_ERROR + + } else return 2; // SDM_ERR_WRONG_BYTES + } + + return 0; // SDM_ERR_NO_ERROR +} + +uint16_t SDM630_calculateCRC(uint8_t *frame, uint8_t num) +{ + uint16_t crc, flag; + crc = 0xFFFF; + for (uint8_t i = 0; i < num; i++) { + crc ^= frame[i]; + for (uint8_t j = 8; j; j--) { + if ((crc & 0x0001) != 0) { // If the LSB is set + crc >>= 1; // Shift right and XOR 0xA001 + crc ^= 0xA001; + } else { // Else LSB is not set + crc >>= 1; // Just shift right + } + } + } + return crc; +} + +/*********************************************************************************************/ + +const uint16_t sdm630_start_addresses[] { + 0x0000, // L1 - SDM630_VOLTAGE [V] + 0x0002, // L2 - SDM630_VOLTAGE [V] + 0x0004, // L3 - SDM630_VOLTAGE [V] + 0x0006, // L1 - SDM630_CURRENT [A] + 0x0008, // L2 - SDM630_CURRENT [A] + 0x000A, // L3 - SDM630_CURRENT [A] + 0x000C, // L1 - SDM630_POWER [W] + 0x000E, // L2 - SDM630_POWER [W] + 0x0010, // L3 - SDM630_POWER [W] + 0x0018, // L1 - SDM630_REACTIVE_POWER [VAR] + 0x001A, // L2 - SDM630_REACTIVE_POWER [VAR] + 0x001C, // L3 - SDM630_REACTIVE_POWER [VAR] + 0x001E, // L1 - SDM630_POWER_FACTOR + 0x0020, // L2 - SDM630_POWER_FACTOR + 0x0022, // L3 - SDM630_POWER_FACTOR + 0x0156 // Total - SDM630_TOTAL_ACTIVE_ENERGY [Wh] +}; + +uint8_t sdm630_read_state = 0; +uint8_t sdm630_send_retry = 0; + +void SDM63050ms() // Every 50 mSec +{ + sdm630_state++; + if (6 == sdm630_state) { // Every 300 mSec + sdm630_state = 0; + + float value = 0; + bool data_ready = SDM630_ModbusReceiveReady(); + + if (data_ready) { + uint8_t error = SDM630_ModbusReceive(&value); + if (error) { + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SDM630 response error %d"), error); + AddLog(LOG_LEVEL_DEBUG); + } else { + switch(sdm630_read_state) { + case 0: + sdm630_voltage[0] = value; + break; + + case 1: + sdm630_voltage[1] = value; + break; + + case 2: + sdm630_voltage[2] = value; + break; + + case 3: + sdm630_current[0] = value; + break; + + case 4: + sdm630_current[1] = value; + break; + + case 5: + sdm630_current[2] = value; + break; + + case 6: + sdm630_active_power[0] = value; + break; + + case 7: + sdm630_active_power[1] = value; + break; + + case 8: + sdm630_active_power[2] = value; + break; + + case 9: + sdm630_reactive_power[0] = value; + break; + + case 10: + sdm630_reactive_power[1] = value; + break; + + case 11: + sdm630_reactive_power[2] = value; + break; + + case 12: + sdm630_power_factor[0] = value; + break; + + case 13: + sdm630_power_factor[1] = value; + break; + + case 14: + sdm630_power_factor[2] = value; + break; + + case 15: + sdm630_energy_total = value; + break; + } // end switch + + sdm630_read_state++; + + if (sizeof(sdm630_start_addresses)/2 == sdm630_read_state) { + sdm630_read_state = 0; + } + } + } // end data ready + + if (0 == sdm630_send_retry || data_ready) { + sdm630_send_retry = 5; + SDM630_ModbusSend(0x04, sdm630_start_addresses[sdm630_read_state], 2); + } else { + sdm630_send_retry--; + } + } // end 300 ms +} + +void SDM630Init() +{ + sdm630_type = 0; + if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { + SDM630Serial = new TasmotaSerial(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX], 1); +#ifdef SDM630_SPEED + if (SDM630Serial->begin(SDM630_SPEED)) { +#else + if (SDM630Serial->begin(2400)) { +#endif + if (SDM630Serial->hardwareSerial()) { ClaimSerial(); } + sdm630_type = 1; + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SDM630_DATA[] PROGMEM = "%s" + "{s}SDM630 " D_VOLTAGE "{m}%s/%s/%s " D_UNIT_VOLT "{e}" + "{s}SDM630 " D_CURRENT "{m}%s/%s/%s " D_UNIT_AMPERE "{e}" + "{s}SDM630 " D_POWERUSAGE_ACTIVE "{m}%s/%s/%s " D_UNIT_WATT "{e}" + "{s}SDM630 " D_POWERUSAGE_REACTIVE "{m}%s/%s/%s " D_UNIT_VAR "{e}" + "{s}SDM630 " D_POWER_FACTOR "{m}%s/%s/%s{e}" + "{s}SDM630 " D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; +#endif // USE_WEBSERVER + +void SDM630Show(boolean json) +{ + char voltage_l1[10]; + char voltage_l2[10]; + char voltage_l3[10]; + char current_l1[10]; + char current_l2[10]; + char current_l3[10]; + char active_power_l1[10]; + char active_power_l2[10]; + char active_power_l3[10]; + char reactive_power_l1[10]; + char reactive_power_l2[10]; + char reactive_power_l3[10]; + char power_factor_l1[10]; + char power_factor_l2[10]; + char power_factor_l3[10]; + char energy_total[10]; + + dtostrfd(sdm630_voltage[0], Settings.flag2.voltage_resolution, voltage_l1); + dtostrfd(sdm630_voltage[1], Settings.flag2.voltage_resolution, voltage_l2); + dtostrfd(sdm630_voltage[2], Settings.flag2.voltage_resolution, voltage_l3); + dtostrfd(sdm630_current[0], Settings.flag2.current_resolution, current_l1); + dtostrfd(sdm630_current[1], Settings.flag2.current_resolution, current_l2); + dtostrfd(sdm630_current[2], Settings.flag2.current_resolution, current_l3); + dtostrfd(sdm630_active_power[0], Settings.flag2.wattage_resolution, active_power_l1); + dtostrfd(sdm630_active_power[1], Settings.flag2.wattage_resolution, active_power_l2); + dtostrfd(sdm630_active_power[2], Settings.flag2.wattage_resolution, active_power_l3); + dtostrfd(sdm630_reactive_power[0], Settings.flag2.wattage_resolution, reactive_power_l1); + dtostrfd(sdm630_reactive_power[1], Settings.flag2.wattage_resolution, reactive_power_l2); + dtostrfd(sdm630_reactive_power[2], Settings.flag2.wattage_resolution, reactive_power_l3); + dtostrfd(sdm630_power_factor[0], 2, power_factor_l1); + dtostrfd(sdm630_power_factor[1], 2, power_factor_l2); + dtostrfd(sdm630_power_factor[2], 2, power_factor_l3); + dtostrfd(sdm630_energy_total, Settings.flag2.energy_resolution, energy_total); + + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" + D_JSON_ACTIVE_POWERUSAGE "\":[%s,%s,%s],\"" D_JSON_REACTIVE_POWERUSAGE "\":[%s,%s,%s],\"" + D_JSON_POWERFACTOR "\":[%s,%s,%s],\"" D_JSON_VOLTAGE "\":[%s,%s,%s],\"" D_JSON_CURRENT "\":[%s,%s,%s]}"), + mqtt_data, energy_total, active_power_l1, active_power_l2, active_power_l3, + reactive_power_l1, reactive_power_l2, reactive_power_l3, + power_factor_l1, power_factor_l2, power_factor_l3, + voltage_l1, voltage_l2, voltage_l3, + current_l1, current_l2, current_l3); +#ifdef USE_WEBSERVER + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_SDM630_DATA, mqtt_data, + voltage_l1, voltage_l2, voltage_l3, current_l1, current_l2, current_l3, + active_power_l1, active_power_l2, active_power_l3, + reactive_power_l1, reactive_power_l2, reactive_power_l3, + power_factor_l1, power_factor_l2, power_factor_l3, energy_total); + } +#endif // USE_WEBSERVER +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XSNS_25 + +boolean Xsns25(byte function) +{ + boolean result = false; + + if (sdm630_type) { + switch (function) { + case FUNC_INIT: + SDM630Init(); + break; + case FUNC_EVERY_50_MSECOND: + SDM63050ms(); + break; + case FUNC_JSON_APPEND: + SDM630Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + SDM630Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif \ No newline at end of file diff --git a/tools/decode-status.py b/tools/decode-status.py new file mode 100644 index 000000000..e94b2cb5f --- /dev/null +++ b/tools/decode-status.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python + +""" + decode-status.py - decode status for Sonoff-Tasmota + + Copyright (C) 2018 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 . + +Requirements: + - Python + - pip json pycurl + +Instructions: + Execute command with option -d to retrieve status report from device or + get a copy of the status message with http command http://sonoff/cm?cmnd=status%200 + and store it in file status.json + +Usage: + ./decode-status.py -d + or + ./decode-status.py -f + +Example: + ./decode-status.py -d sonoff1 + or + ./decode-status.py -f status.json +""" + +import io +import os.path +import json +import pycurl +from sys import exit +from optparse import OptionParser +from StringIO import StringIO + +a_on_off = ["OFF","ON "] + +a_setoption = [ + "Save power state and use after restart", + "Restrict button actions to single, double and hold", + "Show value units in JSON messages", + "MQTT", + "Respond as Command topic instead of RESULT", + "MQTT retain on Power", + "MQTT retain on Button", + "MQTT retain on Switch", + "Convert temperature to Fahrenheit", + "MQTT retain on Sensor", + "MQTT retained LWT to OFFLINE when topic changes", + "Swap Single and Double press Button", + "Do not use flash page rotate", + "Button single press only", + "Power interlock mode", + "Do not allow PWM control", + "Reverse clock", + "Allow entry of decimal color values", + "CO2 color to light signal", + "HASS discovery", + "Do not control Power with Dimmer", + "Energy monitoring while powered off", + "MQTT serial", + "Rules", + "Rules once mode", + "KNX", + "Use Power device index on single relay devices", + "KNX enhancement", + "","","",""] + +a_features = [[ + "","","USE_I2C","USE_SPI", + "USE_DISCOVERY","USE_ARDUINO_OTA","USE_MQTT_TLS","USE_WEBSERVER", + "WEBSERVER_ADVERTISE","USE_EMULATION","MQTT_PUBSUBCLIENT","MQTT_TASMOTAMQTT", + "MQTT_ESPMQTTARDUINO","MQTT_HOST_DISCOVERY","USE_ARILUX_RF","USE_WS2812", + "USE_WS2812_DMA","USE_IR_REMOTE","USE_IR_HVAC","USE_IR_RECEIVE", + "USE_DOMOTICZ","USE_DISPLAY","USE_HOME_ASSISTANT","USE_SERIAL_BRIDGE", + "USE_TIMERS","USE_SUNRISE","USE_TIMERS_WEB","USE_RULES", + "USE_KNX","","",""],[ + "USE_CONFIG_OVERRIDE","BE_MINIMAL","USE_ALL_SENSORS","USE_CLASSIC", + "USE_KNX_NO_EMULATION","","","", + "","","","", + "","","","", + "","","","", + "","","","", + "","","VTABLES_IN_FLASH","PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH", + "PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY","PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH","DEBUG_THEO","USE_DEBUG_DRIVER"],[ + "","USE_ADC_VCC","USE_ENERGY_SENSOR","USE_PZEM004T", + "USE_DS18B20","USE_DS18x20_LEGACY","USE_DS18x20","USE_DHT", + "USE_SHT","USE_HTU","USE_BMP","USE_BME680", + "USE_BH1750","USE_VEML6070","USE_ADS1115_I2CDEV","USE_ADS1115", + "USE_INA219","USE_SHT3X","USE_MHZ19","USE_TSL2561", + "USE_SENSEAIR","USE_PMS5003","USE_MGS","USE_NOVA_SDS", + "USE_SGP30","USE_SR04","USE_SDM120","USE_SI1145", + "USE_SDM630","","",""],[ + "","","","", + "","","","", + "","","","", + "","","","", + "","","","", + "","","","", + "","","","", + "","","",""]] + +usage = "usage: decode-status {-d | -f} arg" +parser = OptionParser(usage) +parser.add_option("-d", "--dev", action="store", type="string", + dest="device", help="device to retrieve status from") +parser.add_option("-f", "--file", metavar="FILE", + dest="jsonfile", default="status.json", help="status json file (default: status.json)") +(options, args) = parser.parse_args() + +if (options.device): + buffer = StringIO() + url = str("http://{}/cm?cmnd=status%200".format(options.device)) + c = pycurl.Curl() + c.setopt(c.URL, url) + c.setopt(c.WRITEDATA, buffer) + c.perform() + c.close() + body = buffer.getvalue() + obj = json.loads(body) +else: + jsonfile = options.jsonfile + fp = open(jsonfile, "r") + obj = json.load(fp) + fp.close() + +def StartDecode(): +# print("Decoding\n{}".format(obj)) + + if ("Time" in obj["StatusSNS"]): + time = str(" from status report taken at {}".format(obj["StatusSNS"]["Time"])) + if ("FriendlyName" in obj["Status"]): + print("\nDecoding information for device {}{}".format(obj["Status"]["FriendlyName"][0], time)) + + if ("SetOption" in obj["StatusLOG"]): + options = [] + option = obj["StatusLOG"]["SetOption"][0] + i_option = int(option,16) + for i in range(len(a_setoption)): + if (a_setoption[i]): + state = (i_option >> i) & 1 + options.append(str("{0:2d} ({1}) {2}".format(i, a_on_off[state], a_setoption[i]))) + + print("\nOptions") + for i in range(len(options)): + print(" {}".format(options[i])) + + + if ("Features" in obj["StatusMEM"]): + features = [] + for f in range(5): + feature = obj["StatusMEM"]["Features"][f] + i_feature = int(feature,16) + if (f == 0): + features.append(str("Language LCID = {}".format(i_feature & 0xFFFF))) + else: + for i in range(len(a_features[f -1])): + if ((i_feature >> i) & 1): + features.append(a_features[f -1][i]) + + features.sort() + print("\nFeatures") + for i in range(len(features)): + print(" {}".format(features[i])) + +if __name__ == "__main__": + try: + StartDecode() + except Exception as e: + print("E: {}".format(e)) \ No newline at end of file diff --git a/tools/fw-server.py b/tools/fw-server.py new file mode 100644 index 000000000..867d89709 --- /dev/null +++ b/tools/fw-server.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +""" + fw-server.py - firmware server for Sonoff-Tasmota OTA upgrade + + Copyright (C) 2018 Gennaro Tortone + + 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 . + +Requirements: + - Python3 + - pip install netifaces flask + +Instructions: + Copy Sonoff-Tasmota firmware binary files in 'fw' directory. + A set of prebuilt files can be downloaded by Sonoff-Tasmota release page: + https://github.com/arendst/Sonoff-Tasmota/releases + + Configure your Sonoff-Tasmota device with your fw-server URL: + Firmware Upgrade -> Upgrade by web server + http://:5000/sonoff-minimal.bin + +Usage: + ./fw-server.py -d (default: eth0) + or + ./fw-server.py -i + +Example: + ./fw-server.py -d wlan0 + or + ./fw-server.py -i 192.168.1.10 +""" + +import io +import os.path +from sys import exit +from flask import Flask, send_file +from optparse import OptionParser +import netifaces as ni + + +usage = "usage: fw-server {-d | -i} arg" +parser = OptionParser(usage) +parser.add_option("-d", "--dev", action="store", type="string", + dest="netdev", default="eth0", help="network interface (default: eth0)") +parser.add_option("-i", "--ip", action="store", type="string", + dest="ip", help="IP address to bind") +(options, args) = parser.parse_args() + +if options.ip is None: + try: + netip = ni.ifaddresses(options.netdev)[ni.AF_INET][0]['addr'] + except Exception as e: + print("E: network interface error - {}".format(e)) + exit(1) +else: + netip = options.ip + +app = Flask(__name__) + +@app.route('/') +def fw(filename): + if(os.path.exists("fw/" + str(filename))): + return send_file("fw/" + str(filename), attachment_filename=filename, mimetype='application/octet-stream') + else: + return("ERROR: file not found") + +if __name__ == "__main__": + try: + app.run(host=netip) + except Exception as e: + print("E: {}".format(e)) diff --git a/tools/fw/README b/tools/fw/README new file mode 100644 index 000000000..c29367442 --- /dev/null +++ b/tools/fw/README @@ -0,0 +1,3 @@ +Copy Sonoff-Tasmota firmware binary files in this directory. +A set of prebuilt files can be downloaded by Sonoff-Tasmota release page: +https://github.com/arendst/Sonoff-Tasmota/releases \ No newline at end of file