Merge branch 'arendst/development' into development

This commit is contained in:
reloxx13 2018-05-25 19:20:57 +02:00
commit d93b9971a5
67 changed files with 2340 additions and 262 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,51 @@
#include <ESP8266WiFi.h>
#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);
}

View File

@ -0,0 +1,52 @@
#include <ESP8266WiFi.h>
#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?");
}

View File

@ -0,0 +1,50 @@
#include <ESP8266WiFi.h>
#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?");
}

View File

@ -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)
#######################################

View File

@ -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"
}

View File

@ -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=*

View File

@ -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<ip_hdr*>(data - UDP_HLEN - IP_HLEN);
ip_addr_t daddr;
daddr.addr = iphdr->dest.addr;
udp_hdr* udphdr = reinterpret_cast<udp_hdr*>(((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<AsyncUDP*>(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<uint8_t*>(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());
}

View File

@ -0,0 +1,130 @@
#ifndef ESPASYNCUDP_H
#define ESPASYNCUDP_H
#include "IPAddress.h"
#include "Print.h"
#include <functional>
#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<void(AsyncUDPPacket& packet)> AuPacketHandlerFunction;
typedef std::function<void(void * arg, AsyncUDPPacket& packet)> 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

View File

@ -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
}

View File

@ -69,4 +69,4 @@ typedef struct __color
uint8_t red;
uint8_t green;
uint8_t blue;
} color_t;
} color_t;

View File

@ -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)

View File

@ -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;

View File

@ -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 <ESPAsyncUDP.h>
// 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://<ip>/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://<ip>/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 <EEPROM.h>
#include <ESP8266WiFi.h>
#ifdef USE_ASYNC_UDP
//#include <ESPAsyncUDP.h>
#else
#include <WiFiUdp.h>
#endif
#include <ESP8266WebServer.h>
#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;

View File

@ -1,5 +1,5 @@
name=ESP KNX IP Library
version=0.5
version=0.5.1
author=Nico Weichbrodt <envy>
maintainer=Nico Weichbrodt <envy>
sentence=ESP8266 library for KNX/IP communication.

View File

@ -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}

View File

@ -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

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 "А"

View File

@ -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 "安"

View File

@ -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 "安"

View File

@ -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 {

View File

@ -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);

View File

@ -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 <MQTT_TOPIC>-<last 4 decimal chars of MAC address>
@ -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 };

View File

@ -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 <core_version.h> // 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;

View File

@ -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;
/********************************************************************************************/

View File

@ -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();
}
}
}

View File

@ -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)

View File

@ -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 =
"<!DOCTYPE html><html lang=\"" D_HTML_LANGUAGE "\" class=\"\">"
"<head>"
@ -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("</div><br/>");
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); }

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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); }

View File

@ -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
#endif // USE_RULES

View File

@ -48,21 +48,26 @@ byte Settings.knx_CB_param[MAX_KNX_CB] Type of Output (set relay, t
\*********************************************************************************************/
#include <esp-knx-ip.h>
#include <esp-knx-ip.h> // KNX Library
// Note: Inside the <esp-knx-ip.h> 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 =
"<br/><br/>" D_KNX_PHYSICAL_ADDRESS_NOTE "<br/><br/>"
"<input style='width:10%;' id='b1' name='b1' type='checkbox'";
const char HTTP_FORM_KNX1[] PROGMEM =
"><b>" D_KNX_ENABLE " </b><input style='width:10%;' id='b2' name='b2' type='checkbox'";
const char HTTP_FORM_KNX2[] PROGMEM =
"><b>" D_KNX_ENABLE "</b><br/></center><br/>"
"><b>" D_KNX_ENHANCEMENT "</b><br/></center><br/>"
"<fieldset><center>"
"<b>" D_KNX_GROUP_ADDRESS_TO_WRITE "</b><hr>"
@ -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;

View File

@ -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)

View File

@ -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,"

355
sonoff/xsns_25_sdm630.ino Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifdef USE_SDM630
/*********************************************************************************************\
* Eastron SDM630-Modbus energy meter
*
* Based on: https://github.com/reaper7/SDM_Energy_Meter
\*********************************************************************************************/
#include <TasmotaSerial.h>
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

183
tools/decode-status.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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 <hostname or IP address>
or
./decode-status.py -f <JSON status information file>
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))

83
tools/fw-server.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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://<ip_address>:5000/sonoff-minimal.bin
Usage:
./fw-server.py -d <net_iface> (default: eth0)
or
./fw-server.py -i <ip_address>
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('/<filename>')
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))

3
tools/fw/README Normal file
View File

@ -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