How to use TASMOTA inside BLYNK and HOME ASSISTANT [Work in Progress]

You’ve been busy!

The “Bedroom Strip” with a bit of Tina’s ‘Private Dancer’ playing in the background certainly sounds interesting! :rofl::rofl::rofl:

Pete.

1 Like

Device ID & RSSI are shown here



HA Components are growing every day All (1310)


Components for:

WeMo

Magic Home

ONVIF Camera

For Sonoff there is “No component yet”

I am using a custom component that works fine.

Home Assistant component for original firmware Sonoff / eWeLink smart devices

I am receiving warnings from HA but every thing is OK

You are using a custom component for sonoff.switch which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant.

3:21 PM loader.py (WARNING)

You are using a custom component for sonoff.sensor which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant.

3:21 PM loader.py (WARNING)

Compatibility list

Model Supported 1.6 1.8.1 2.6 2.6.1 2.7.1 3.0.0 Remarks
Sonoff Basic yes yes yes yes yes
Sonoff Dual yes
Sonoff RF yes yes yes
Sonoff G1 ?
Sonoff 4CH Pro yes yes yes
Sonoff 4CH Pro R2 yes yes
Sonoff S20 yes yes yes
Sonoff S31 yes
Sonoff S26 yes yes yes version: Euro
Sonoff T1 1C yes yes
Sonoff T1 EU 2C yes yes
Sonoff T1 UK 3C yes yes yes
Sonoff T1 US 3C yes
Sonoff Pow yes + power sensor
Sonoff Pow R2 yes + power/current/voltage sensors
Sonoff TH10/TH16 yes + temp/humidity sensors
Sonoff iFan02 yes it creates 4 switches, 1 for the light and 3 for the various fan speeds
Sonoff HT-TH31 ?
Sonoff Slampher RF yes yes yes
3 Gang Generic Wall Switch yes yes Manfufacturer: pro-sw, Model: PS-15-ES (according to ewelink app)
1 Gang Generic Wall Switch yes yes yes manfufacturer: KingART, model: KING-N1 (according to ewelink app), Chip: PSF-B85 (ESP8285)
WHDTS WiFi Momentary Inching Relay yes displayed as a switch button
MHCOZY WiFi Wireless 5V/12V yes
Geekcreit 2 Channel AC 85V-250V yes yes
Smart Wi-Fi Outlet yes

yes = confirmed version, [empty] = unknown for sure

As we see all components are using .py extension to work with HA

HASS-sonoff-ewelink/sensor/ sonoff.py

HASS-sonoff-ewelink/switch/ sonoff.py


@PeteKnight
@Emilio

Here I am asking if there is anyone on this forum having a good knowledge of python programming language to help us translating this to BLYNK without the need to change the SONOFF firmware to TASMOTA to be able to use MQTT publish and subscribe message protocol.

Continuing the discussion from Hacking the new ITEAD Studio - Sonoff S31

Today I think I knew how Peter Buga can control Sonoff / eWeLink smart devices.

Download and install Python 2.x or 3.x from https://www.python.org/downloads

Go to sonoff-debug.py
To run this just, cd to the location of the script and:

python sonoff-debug.py -u 'email or phone-number username' -p 'password' > devices.json

you will get a file named “devices.json”

this is one of my Sonoff POW R2 but the file contain all Sonoffs in my Home.
if you take a close look to the file you will find all the variables power, voltage, current, rssi, device id, and more…

[
  {
    "__v": 0, 
    "_id": "[hidden]", 
    "apikey": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 
    "brandLogoUrl": "", 
    "brandName": "Sonoff", 
    "createdAt": "xxxx-xx-xxxxx:xx:xx.xxx", 
    "deviceStatus": "", 
    "deviceUrl": "", 
    "deviceid": "[hidden]", 
    "devicekey": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 
    "extra": {
      "_id": "[hidden]", 
      "extra": {
        "apmac": "xx:xx:xx:xx:xx:xx", 
        "brandId": "58e5f344baeb368720e25469", 
        "description": "WO1881982", 
        "mac": "xx:xx:xx:xx:xx:xx", 
        "manufacturer": "\u6df1\u5733\u677e\u8bfa\u6280\u672f\u6709\u9650\u516c\u53f8", 
        "model": "PSC-B67-GL", 
        "modelInfo": "5a2e1ae50cf772f92c342ef6", 
        "ui": "\u529f\u7387\u68c0\u6d4b\u63d2\u5ea7\u8fc7\u8f7d\u544a\u8b66", 
        "uiid": 32
      }
    }, 
    "group": "", 
    "groups": [], 
    "ip": "[hidden]", 
    "location": "", 
    "name": "[hidden]",, 
    "offlineTime": "xxxx-xx-xxxxx:xx:xx.xxx", 
    "online": true, 
    "onlineTime": "xxxx-xx-xxxxx:xx:xx.xxx", 
    "params": {
      "alarmCValue": [
        -1, 
        10
      ], 
      "alarmPValue": [
        -1, 
        -1
      ], 
      "alarmType": "pcv", 
      "alarmVValue": [
        -1, 
        240
      ], 
      "bindInfos": {
        "alexa": [
          "xxxxxx-xxxx-xxxxx-xxxxx-xxxxxxxxxxxxx_26ca1996a20e8bd63617axxxxxxxxxxxxxxxxxxxx"
        ], 
        "gaction": [
          "xxxxxxxxxx-xxxxx-xxxxxx-xxxxxxxxxxx_ewelink-google-home-v1"
        ]
      }, 
      "current": "0.28", 
      "endTime": "xxxx-xx-xxxxx:xx:xx.xxx", 
      "fwVersion": "2.8.0", 
      "hundredDaysKwh": "get", 
      "init": 1, 
      "oneKwh": "stop", 
      "partnerApikey": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 
      "power": "59.81", 
      "rssi": -71, 
      "sledOnline": "on", 
      "staMac": "xx:xx:xx:xx:xx:xx", 
      "startTime": "xxxx-xx-xxxxx:xx:xx.xxx", 
      "startup": "stay", 
      "switch": "on", 
      "timeZone": 2, 
      "voltage": "234.57"
    }, 
    "productModel": "Pow_R2", 
    "settings": {
      "alarmNotify": 1, 
      "opsHistory": 1, 
      "opsNotify": 0
    }, 
    "sharedTo": [
      {
        "nickname": "Pico", 
        "note": "", 
        "permit": 15, 
        "phoneNumber": "[hidden]",, 
        "shareTime": 1551282301502
      }
    ], 
    "showBrand": true, 
    "type": "10", 
    "uiid": 32
  }, 

to remove all hidden information in this file go to sonoff-debug.py and comment

data = re.sub(r'"phoneNumber": ".*"', '"phoneNumber": "[hidden]",', data)
data = re.sub(r'"name": ".*"', '"name": "[hidden]",', data)
data = re.sub(r'"ip": ".*",', '"ip": "[hidden]",', data)
data = re.sub(r'"deviceid": ".*",', '"deviceid": "[hidden]",', data)
data = re.sub(r'"_id": ".*",', '"_id": "[hidden]",', data)
data = re.sub(r'"\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2}"', '"xx:xx:xx:xx:xx:xx"', data)
data = re.sub(r'"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}"', '"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"', data)
data = re.sub(r'"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z"', '"xxxx-xx-xxxxx:xx:xx.xxx"', data)

before running the script .


at end I think https://community.home-assistant.io/u/peterbuga has done a great job
and here is what he wrote:
the data that you see/get, it’s exactly what I get from eWeLink API and nothing more.
I recently found out that sensor data is propagated over websocket messages too, not just basic requests (as it now), when I do find the time I’ll update the component to take advantage of this and hopefully an overall better output :crossed_fingers:

Back to “MQTT”

Understanding MQTT: How Smart Home Devices Communicate


Add a door sensor to any door with a Sonoff basic.


Fix Random Switching in Tasmota by Adjusting Retain Settings

Effectively, he recommends :

  1. Make sure you have set the retain to false in Home Assistant Configuration.

  2. Restart the Home Assistant server .

  3. Send these commands to the Sonoff in the console menu.

    switchretain off
    buttonretain on
    buttonretain off
    poweronstate 3
    powerretain on

IF you don’t have a Raspberry PI to install Mosquitto broker and need an MQTT that works with BLYNK here is the cheapest and easiest solution.

The “ESP uMQTT Broker”

MQTT Broker/Bridge on the ESP8266


the CODE works on any ESP8266 NodeMCU, Wemo D1 ,Sonoff Basic (Tested)

// uMQTTBroker for Sonoff
// 
// The program simply starts a broker and waits for any client to connect.
// maximal 10 Client possible
// 
///////////////////////////////////////////////////////////////////////////////

#include "ESP8266WiFi.h"
#include "uMQTTBroker.h"
#include "espconn.h"

// Your WiFi config here //////////////////////////////////////////////////////
char host[] = "ESP-MQTT";           // Hostname
char ssid[] = "ssid";               // your network SSID (name)
char pass[] = "password";           // your network password
bool STATIC = false;                // Static IP for STA Mode?   [false/true]
IPAddress ip(192,168,1,200);        // Static IP
IPAddress gw(192,168,1,1);          // Gateway
IPAddress su(255,255,255,0);        // Subnet
bool WiFiAP = false;                // Do yo want the ESP as AP? [false/true]
bool EXAMPL = true;                 // Example Progamm on/off?   [false/true]
// Example need rule in Test_1
// rule1 on power1#state=1 do publish stat/TEST_1/POWER TRUE endon on power1#state=0 do publish stat/TEST_1/POWER FALSE endon

// Declare Variable ///////////////////////////////////////////////////////////
String RECEIVE;                     // Received Topic for logical operations
String RECEVAL;	                    // Received Topic for logical operations
int GPIO0 = 0;                      // GPIO0  -> Sonoff Basic - Button
int GPIO12 = 12;                    // GPIO12 -> Sonoff Basic - Relais
int GPIO13 = 13;                    // GPIO13 -> Sonoff Basic - green LED
int GPIO14 = 14;                    // GPIO14 -> Sonoff Basic - Optional Sensor
int BSTAT1;                         // State Button GPIO0	
int RSTAT1;                         // State Relais GPIO12	
int TRIGG1 = LOW;

class myMQTTBroker: public uMQTTBroker
{
public:
    virtual bool onConnect(IPAddress addr, uint16_t client_count) {
      Serial.println(addr.toString()+" connected");
      return true;
    }
    
    virtual bool onAuth(String MQTTUSER, String MQTTPASS) {
      Serial.println("Username/Password: "+MQTTUSER+"/"+MQTTPASS);
      return true;
    }
    
    virtual void onData(String topic, const char *data, uint32_t length) {
      char data_chr[length+1];
      os_memcpy(data_chr, data, length);
      data_chr[length] = '\0';
      String data_str = (String)data_chr; 
      data_str.toUpperCase();
      
      RECEVAL = topic+" "+data_str;
      Serial.println("received: "+RECEVAL);
    }
};

void MQTT_Server_cleanupClientCons();

myMQTTBroker myBroker;

// WiFi init stuff ////////////////////////////////////////////////////////////
void startWiFiClient()
{
  if (STATIC) {
	WiFi.config(ip, gw, gw, su);
  }
  WiFi.softAPdisconnect(true);
  WiFi.hostname(host);
  Serial.println("Connecting to "+(String)ssid);
  WiFi.begin(ssid, pass);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  
  Serial.println("WiFi connected");
  Serial.println("IP address: " + WiFi.localIP().toString());
}

void startWiFiAP()
{
  WiFi.softAP(ssid, pass);
  Serial.println("AP started");
  Serial.println("IP address: " + WiFi.softAPIP().toString());
}

void setup()
{
  //  Configure GPIO //////////////////////////////////////////////////////////
  pinMode(GPIO0, INPUT_PULLUP);
  pinMode(GPIO12, OUTPUT);
  pinMode(GPIO13, OUTPUT);
  pinMode(GPIO14, OUTPUT);
  
  // Serial Interface  
  Serial.begin(115200);
  Serial.println();
  Serial.println();

  // Start WiFi
  espconn_tcp_set_max_con(10);
  if (WiFiAP) {
    startWiFiAP();
  } else {
    startWiFiClient();
  }

  // Start the broker
  Serial.println("Starting MQTT broker");
  myBroker.init();

  // Subscribe to anything ////////////////////////////////////////////////////
  myBroker.subscribe("#");
}

void loop()
{
  RECEIVE = RECEVAL;

  // Example Programm /////////////////////////////////////////////////////////  
  if (EXAMPL) {
  if (RECEIVE.equals("stat/TEST_1/POWER FALSE")) {
    myBroker.publish("cmnd/TEST_2/POWER", "ON");
  }
  if (RECEIVE.equals("stat/TEST_1/POWER TRUE")) {
    myBroker.publish("cmnd/TEST_2/POWER", "OFF");
  }
  }
  
  if (RECEIVE.equals("cmnd/"+(String)host+"/STATUS 1")) {
    myBroker.publish("cmnd/TEST_1/POWER", "(null)");
    myBroker.publish("cmnd/TEST_2/POWER", "(null)");
    if (RSTAT1 == LOW) {
      myBroker.publish("stat/"+(String)host+"/POWER", "OFF");
    } else {
      myBroker.publish("stat/"+(String)host+"/POWER", "ON");
    }
  }
  
  /////////////////////////////////////////////////////////////////////////////
  // Basic Function Button/MQTT ///////////////////////////////////////////////
  BSTAT1 = digitalRead(GPIO0);
  RSTAT1 = digitalRead(GPIO12);
  
  // Button event /////////////////////////////////////////////////////////////
  if (BSTAT1 == LOW and RSTAT1 == LOW and TRIGG1 == LOW) {
     digitalWrite(GPIO12, HIGH);
     TRIGG1 = HIGH;
     Serial.println("GPIO0 POWER ON");
     myBroker.publish("stat/"+(String)host+"/POWER", "ON");
  }
  if (BSTAT1 == LOW and RSTAT1 == HIGH and TRIGG1 == LOW) {
     digitalWrite(GPIO12, LOW);
     TRIGG1 = HIGH;
     Serial.println("GPIO0 POWER OFF");
     myBroker.publish("stat/"+(String)host+"/POWER", "OFF");
  }
  
  if (BSTAT1 == HIGH) { TRIGG1 = LOW; }
  
  // MQTT event ///////////////////////////////////////////////////////////////
  if (RECEIVE.equals("cmnd/"+(String)host+"/POWER ON")) {
    digitalWrite(GPIO12, HIGH);
    Serial.println("stat/"+(String)host+"/POWER ON");
    myBroker.publish("stat/"+(String)host+"/POWER", "ON");
    delay(50);
  }
  if (RECEIVE.equals("cmnd/"+(String)host+"/POWER OFF")) {
    digitalWrite(GPIO12, LOW);
    Serial.println("stat/"+(String)host+"/POWER OFF");
    myBroker.publish("stat/"+(String)host+"/POWER", "OFF");
    delay(50);
  }
  if (RECEIVE.equals("cmnd/"+(String)host+"/POWER ")) {
    if (RSTAT1 == LOW) {
      myBroker.publish("stat/"+(String)host+"/POWER", "OFF");
    } else {
      myBroker.publish("stat/"+(String)host+"/POWER", "ON");
    }
  }

  // LED STATUS ///////////////////////////////////////////////////////////////
  if (RSTAT1 == LOW) {	
   digitalWrite(GPIO13, HIGH);
  } else {
   digitalWrite(GPIO13, LOW);
  }
}

you can test it with

IoT MQTT Dashboard

https://play.google.com/store/apps/details?id=com.thn.iotmqttdashboard

another Link


I even test “uMQTT broker” with HA and it works OK

==============================================================================

MQTT-Explorer for Windows


4 Likes

TASMOTA running on NodeMCU controlled by “BLYNK” and Home Assistant through MQTT




23:53:24 MQT: stairs/Distance = {"Distance":40.842}
23:53:24 MQT: stairs/tele/STATE = {"Time":"2019-03-19T23:53:24","Uptime":"0T01:11:57","Vcc":2.973,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":43,"POWER1":"ON","POWER2":"OFF","POWER3":"ON","Wifi":{"AP":1,"SSId":"BLYNK","BSSId":"48:F8:B3:84:AB:22","Channel":10,"RSSI":70,"LinkCount":1,"Downtime":"0T00:00:04"}}
23:53:24 MQT: stairs/tele/SENSOR = {"Time":"2019-03-19T23:53:24","Switch1":"OFF","DS18B20-1":{"Id":"000006F242B1","Temperature":21.5},"DS18B20-2":{"Id":"000006F2B80A","Temperature":21.6},"DS18B20-3":{"Id":"00000789E508","Temperature":21.5},"DS18B20-4":{"Id":"8000001F18D8","Temperature":21.5},"AM2301":{"Temperature":21.3,"Humidity":69.5},"SR04":{"Distance":40.404},"TempUnit":"C"}
23:53:24 MQT: stairs/tele/STATE = {"Time":"2019-03-19T23:53:24","Uptime":"0T01:11:57","Vcc":2.980,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":77,"POWER1":"ON","POWER2":"OFF","POWER3":"ON","Wifi":{"AP":1,"SSId":"BLYNK","BSSId":"48:F8:B3:84:AB:22","Channel":10,"RSSI":68,"LinkCount":1,"Downtime":"0T00:00:04"}}
23:53:24 MQT: stairs/stat/RESULT = {"POWER2":"OFF"}
23:53:24 MQT: stairs/stat/POWER2 = OFF
23:53:26 RUL: SR04#DISTANCE>%VAR2% performs "backlog  publish stairs/Distance {"Distance":40.456};power2 off"

00:03:06 MQT: stairs/stat/RESULT = {"POWER1":"ON"}
00:03:06 MQT: stairs/stat/POWER1 = ON
00:03:06 MQT: stairs/stat/RESULT = {"T1":30,"T2":0,"T3":0,"T4":0,"T5":0,"T6":0,"T7":0,"T8":0}
00:03:06 RUL: SR04#DISTANCE<%VAR2% performs "backlog  publish stairs/Distance {"Distance":17.298};power2 on"
00:03:06 MQT: stairs/Distance = {"Distance":17.298}

“BLYNK” Tasmota Project _# 1

controlling NodeMCU flashed by a recent Tasmota bin.file or (sketch) as explained in post #2

  • 4x DS18B20 Temperature Sensor
  • 1x DHT22 (DHT21, AM2301, AM2302, AM2321)Temperature and Humidity Sensor
  • 1x HC-SR501 PIR Motion Sensor
  • 1x HC-SR04 Ultrasonic Sensor
  • 1x SSD1306 I2C OLED Display

BLYNK is running on another NodeMCU or simply an ESP8266-01 because in this example we don’t need hardware pins.

before we continue the most important is to send our Rules to Tasmota and be sure it is acting as we plan.

  1. My project is Stair lights (Power1) will turn on if PIR detects a motion (Auto Mode) and lights will remain on for 30 sec. if PIR is triggered again before the 30 sec counter finish the lights will remain on for another 30 sec and so on until there is no more movement .
  2. The ability to stop the PIR (Manual Mode)
  3. Stair lights can also be controlled by the SR04 ultrasonic sensor (Distance change)
  4. Also the ability to stop the US sensor.
  5. Porch lights (Power2) turns OFF with sunrise and ON at sunset.
  6. Reading of all Temperature and Humidity sensors.
  • In this project I set Telemetry Period to 30 sec the default is 300 sec or 5 minutes



Open Tasmota main Menu then go to Console menu and send Backlog then Rules

  • Backlog

Backlog PowerRetain 1; module 18; gpio00 17; gpio01 04; gpio2 29; gpio3 02; gpio4 06; gpio5 05; gpio12 73; gpio13 74; gpio14 82; gpio15 00; gpio16 30; setoption8 0; teleperiod 30

  • RULES

    mem1 30
    switchmode1 1
    Rule1 on switch1#state do publish stairs/PIR_stairs/state %value% endon
    rule1 1
    
    Rule2 on Switch1#state=1 do backlog event AutoMotion=%mem2%; publish stairs/mem2/state %mem2% endon on event#AutoMotion==1 do backlog power1 on; ruletimer1 %mem1% endon on rules#timer=1 do power1 off endon on event#AutoMotion==0 do RuleTimer1 0 endon
    
    Rule3  on SR04#Distance<%var2% do backlog  publish stairs/Distance {"Distance":%value%};power1 on; ruletimer2 %mem1% endon on rules#timer=2 do power1 off endon on SR04#Distance do backlog  publish stairs/Distance {"Distance":%value%} endon
    
  • Be sure to write each Rule on one line.

  • After sending a rule you must turn it on by
    Rule(x) 1
    (x) is 1 or 2 or 3

  • To turn OFF a Rule.
    Rule1 0

  • for Tasmota to be discovered by Home Assistant.
    setoption19 1

  • BLYNK ENERGY = 1800
    Your Display may differ from mine but you can easily adjust it


NODE_MQTT_PIR_TEMP.ino

  #include <ESP8266WiFi.h>
  #include <ESP8266mDNS.h>
  #include <WiFiUdp.h>
  #include <ArduinoOTA.h>
  #include <PubSubClient.h>
  #include <ArduinoJson.h>
  
  #include <BlynkSimpleEsp8266.h>
  #define BLYNK_PRINT Serial    // Comment this out to disable prints and save space  

  #include "Settings.h"
  #include "mqtt_publish.h"
  #include "timer.h"    
  #include "mqtt_subscribe.h"    
  #include "wifi_credentials.h"
/*-------------------------------- Setup -------------------------------------*/
  void setup() {
    WiFi.mode(WIFI_STA);
    Serial.begin(115200);  // See the connection status in Serial Monitor
    Serial.println(F("\n Setup STARTING"));
    WiFi.begin(WIFI_SSID, WIFI_PASS);
    if(WiFi.status() == 6) Serial.println("\tWiFi not connected yet.");

//----------------------------------Blynk------------------------------------
    #ifdef LOCAL_SERVER
      Blynk.config(AUTH, LOCAL_SERVER ,8080);
    #else
      Blynk.config(AUTH, Geo_DNS_SERVER);
    #endif
    while (Blynk.connect() == false) {             // Wait until connected
      if ((millis() - last_UP_change) > 30000) {   // 30s before reset
        Serial.println(F("resetting"));
        ESP.restart();
      } 
    } 

    client.setServer(MQTT_SERVER, 1883);
    client.setCallback(callback);
//-----------------------------------OTA--------------------------------------
    ArduinoOTA.setHostname(OTA_HOSTNAME);
//  ArduinoOTA.setPassword(OTA_PASSWORD);  //If you need a Password uncomment this line
    ArduinoOTA.begin();

    Serial.println(F(" Entering loop"));
    timer.setInterval(4000L, Heartbeat);       // Heartbeat 4000L

    CountdownTimer = timer.setInterval(1000, CountdownTimerFunction);
    timer.disable(CountdownTimer); // disable it on boot

    Blynk.syncVirtual(vPIN_ON_OFF_1);
    Blynk.syncVirtual(vPIN_ON_OFF_2);
    Blynk.syncVirtual(vPIN_AUTO_MOTION);
    Blynk.syncVirtual(vPIN_NUMERIC_TIMER);

    Blynk.virtualWrite(vPIN_COUNTDOWN,  "  0:00:00" );
    Blynk.setProperty(vPIN_ON_OFF_1, "label",  String("T1 = ") + "0:00:00" + String(" S") );
    Blynk.setProperty(vPIN_PIR_LED, "label",  String ("PIR <---- ") + memory1 + String(" S") );
    Blynk.setProperty(vPIN_HEARTBEAT, "label",  String("\xF0\x9F\x92\x93") + " HEARTBEAT");
  }

/*----------------------------------Loop--------------------------------------*/

  void loop() {
    Blynk.run();
    if(!Blynk.connected()) {
      Serial.println(F("Resetting in loop"));
      ESP.restart();
    }
    timer.run();
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
    ArduinoOTA.handle();
  }

/*=============================================================================*/

Settings.h

/*****************************************************************************
 *
 *                             Settings.h
 *
 *****************************************************************************/


/*---------------------------Blynk Auth Code----------------------------------*/
  #define AUTH   "*******************************************" // NodeMCU

 /*-------------------------Over The Air Hostname------------------------------*/

  #define OTA_HOSTNAME          "N-MQTT_Bridge"   // put here your host name
  #define OTA_PASSWORD          "blynk"     // your password for OTA if needed

/*----------------------------Hardware Pins-----------------------------------*/
                             /*-NodeMCU#*/

/*-------------------------------Virtual Pins---------------------------------*/
   
  #define vPIN_HEARTBEAT          V0
  #define vPIN_DS18B20_1_TEMP     V1
  #define vPIN_DS18B20_2_TEMP     V2
  #define vPIN_DS18B20_3_TEMP     V3
  #define vPIN_DS18B20_4_TEMP     V4
  #define vPIN_AM2301_TEMP        V5
  #define vPIN_AM2301_HUM         V6
  #define vPIN_SR04_DIST          V7
  #define vPIN_PIR_LED            V8
  #define vPIN_TEMP_UNIT          V9

  #define vPIN_HUM                V10
  #define vPIN_ALL_TEMP           V11

  #define vPIN_ON_OFF_1           V21     // ON/OFF POWER 1  Activated by Motion (Tasmota Rule1 & Rule2)
  #define vPIN_AUTO_MOTION        V22     // when motion is detected Relay1 turns on for timer sec & repeats 
  #define vPIN_NUMERIC_TIMER      V23     // up to 32767 ~9hrs
  #define vPIN_COUNTDOWN          V24     // triggered by motion countdown for timer sec and restart with each PIR trigger
  #define vPIN_AUTO_SR04          V25     // Activate/Desactivate Rule3

  #define vPIN_ON_OFF_2           V31     // ON/OFF POWER 2  Activated by Sun  
/*-----------------------------variables-------------------------------------*/

  bool  memory2 ;
  int   memory1 ;
  int   counter = 0;
  int   CountdownTimer;
  int   CountdownRemain;
  long int  last_UP_change = millis();
  
  int   rssi;
  float Vcc;
  float Distance;

mqtt_publish.h

/******************************************************************************
 *
 *                            mqtt_publish.h
 *
 *****************************************************************************/
  WiFiClient espClient;
  PubSubClient client(espClient);

  BlynkTimer timer;
//----------------------------------------------℃/℉-----------------------------------------------------------
  BLYNK_WRITE(vPIN_TEMP_UNIT) {  // Change Temp. Unit from Celsius ℃ to Fahrenheit 
    if (param.asInt()) {
      client.publish("stairs/cmnd/setoption8","1"); // Fahrenheit ℉
      Blynk.setProperty(vPIN_TEMP_UNIT, "label",String("\xf0\x9f\x8c\xa1") + "Fahrenheit ℉");
    } else {
      client.publish("stairs/cmnd/setoption8","0"); // Celcius ℃
      Blynk.setProperty(vPIN_TEMP_UNIT, "label",String("\xf0\x9f\x8c\xa1") + " Celsius ℃");
    }
  }
//---------------------------------------------POWER1----(Motion)-------------------------------------------
  BLYNK_WRITE(vPIN_ON_OFF_1) {    // ON/OFF switch
    if (param.asInt()) {
      client.publish("stairs/cmnd/POWER1","1");
    } else {
      client.publish("stairs/cmnd/POWER1","0");
    }
  }

  BLYNK_WRITE(vPIN_AUTO_MOTION) {  // AUTO/Manual
    if (param.asInt()) {
      client.publish("stairs/cmnd/mem2","1");
      Blynk.setProperty(vPIN_AUTO_MOTION, "label",String("\xF0\x9F\x92\xAB") + "    AUTO    PIR");
      memory2 = 1;
    } else {
      client.publish("stairs/cmnd/mem2","0");
      Blynk.setProperty (vPIN_AUTO_MOTION, "label",String("\xE2\x9C\x8B") + "    MANUAL    PIR");
      memory2 = 0;
      CountdownRemain = 1;
    }
  }

  BLYNK_WRITE(vPIN_NUMERIC_TIMER) { // TIMER 0-300 Sec  up to 32767sec [9hr:06min:07sec] 
   int VALUE = param.asInt();
    char value[4];
    dtostrf(VALUE, 3, 0, value);
    client.publish("stairs/cmnd/Mem1", value);
    Blynk.setProperty(vPIN_PIR_LED, "label",  String("PIR <---- ") + value + String(" S") );
    if (memory2 == 1) { 
      client.publish("stairs/cmnd/POWER1", "ON");
    } else {
      CountdownRemain = 1;
    }
  }
//-----------------------------------------------SR04---------------------------------------------------
  BLYNK_WRITE(vPIN_AUTO_SR04) {  // AUTO/Manual
    if (param.asInt()) {
      client.publish("stairs/cmnd/Rule3","1");
      Blynk.setProperty(vPIN_AUTO_SR04, "label",String("\xF0\x9F\x92\xAB") + "    AUTO    SR04");
    } else {
      client.publish("stairs/cmnd/Rule3","0");
      Blynk.setProperty (vPIN_AUTO_SR04, "label",String("\xE2\x9C\x8B") + "    MANUAL    SR04");
    }
  }
//---------------------------------------------POWER2----(Sun)-------------------------------------------
  BLYNK_WRITE(vPIN_ON_OFF_2) {    // ON/OFF switch
    if (param.asInt()) {
      client.publish("stairs/cmnd/POWER2","1");
    } else {
      client.publish("stairs/cmnd/POWER2","0");
    }
  }

/*--------------------------------Heartbeat--------------------------------*/

  void Heartbeat() {
    Blynk.virtualWrite(vPIN_HEARTBEAT,"    \xF0\x9F\x92\x96"); //  
timer.setTimeout(2000L,[](){Blynk.virtualWrite(vPIN_HEARTBEAT,"    \xF0\x9F\x92\x97");}); 
    }

/*--------------------------------------------------------------------------*/

mqtt_subscribe.h

/*******************************************************************************
 *
 *                            mqtt_subscribe.h
 *
 ******************************************************************************/
  #define MQTT_SERVER        IPAddress(192,168,xxx,xxx) // Your MQTT Broker IP address
  #define MQTT_USERNAME      "xxxx"   // put here your MQTT username
  #define MQTT_PASSWORD      "xxxx"    // put here your MQTT password
  
  #define InTOPIC_0          "stairs/tele/STATE" 
  #define InTOPIC_1          "stairs/tele/SENSOR" 
  #define InTOPIC_2          "stairs/PIR_stairs/state" 
  #define InTOPIC_3          "stairs/stat/POWER1" 
  #define InTOPIC_4          "stairs/stat/POWER2" 
  #define InTOPIC_5          "stairs/Distance" 
  #define InTOPIC_6          "stairs/stat/RESULT" 
  

  void reconnect() {      // Loop until we're reconnected
    while (!client.connected()) {
      Serial.print(" Attempting MQTT connection...");   // Attempt to connect
      if (client.connect("DVES",MQTT_USERNAME,MQTT_PASSWORD)) {
        Serial.println("connected");
        counter = 0;     // ... and resubscribe
        client.subscribe(InTOPIC_0);
        client.subscribe(InTOPIC_1);
        client.subscribe(InTOPIC_2);
        client.subscribe(InTOPIC_3);
        client.subscribe(InTOPIC_4);
        client.subscribe(InTOPIC_5);
        client.subscribe(InTOPIC_6);
      } else {
        Serial.print("failed, rc=");
        Serial.print(client.state());
        Serial.println(" try again in 0.3 second");
        ++counter;
        if (counter > 180) ESP.reset();     // Wait .3 seconds before retrying
        delay(300);
      }
    }
  }

  void callback(char* topic, byte* payload, unsigned int length) { 
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++) {
      Serial.print((char)payload[i]);
    }
    Serial.println();
    Serial.println();
//-------------------------------------------STATE------------------------------------------------
//  MQT: stairs/tele/STATE = 
//  {"Time":"2019-03-18T17:56:18","Uptime":"0T18:23:11","Vcc":3.002,"SleepMode":"Dynamic",
//  "Sleep":50,"LoadAvg":19,"POWER1":"ON","POWER2":"OFF","Wifi":{"AP":1,"SSId":"BLYNK",
//  "BSSId":"48:F8:B3:84:AB:22","Channel":10,"RSSI":84,"LinkCount":6,"Downtime":"0T00:00:21"}}

    if (String(topic) == InTOPIC_0) {   // stairs/tele/STATE
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
                    Vcc = root["Vcc"];            // 3.002
                    rssi = root["Wifi"]["RSSI"];  // 84

//        const char* Time = root["Time"];          // 2019-03-18T17:56:18
//        const char* Uptime = root["Uptime"];      // 0T18:23:11
//        const char* SleepMode = root["SleepMode"];// Dynamic
//               int  Sleep = root["Sleep"];        // 50
//               int  LoadAvg = root["LoadAvg"];    // 19
//        const char* POWER1 = root["POWER1"];      // ON
//        const char* POWER2 = root["POWER2"];      // OFF
//               int  AP = root["Wifi"]["AP"];      // 1  
//        const char* SSId = root["Wifi"]["SSId"];  // BLYNK
//        const char* BSSId = root["Wifi"]["BSSId"];// 48:F8:B3:84:AB:22
//               int  Chl = root["Wifi"]["Channel"];// 10  
//               int  LC= root["Wifi"]["LinkCount"];// 6
//        const char* DT = root["Wifi"]["Downtime"];// 0T00:00:21

//-------------------------------------------SENSOR-----------------------------------------------
//MQT: stairs/tele/SENSOR = 
//{"Time":"2019-03-19T13:19:16","Switch1":"OFF","DS18B20-1":{"Id":"000006F242B1","Temperature":21.9},
//"DS18B20-2":{"Id":"000006F2B80A","Temperature":22.0},"DS18B20-3":{"Id":"00000789E508","Temperature":21.9},
//"DS18B20-4":{"Id":"8000001F18D8","Temperature":22.6},"AM2301":{"Temperature":22.0,"Humidity":68.7},
//"ADS1115":[{"A0":-1,"A1":26033,"A2":4851,"A3":4809}],"SR04":{"Distance":40.509},"TempUnit":"C"}

    }else if (String(topic) == InTOPIC_1) { // stairs/tele/SENSOR
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        const char* Time = root["Time"];                      // 2019-03-19T13:19:16
             float  DS1_T = root["DS18B20-1"]["Temperature"]; // 21.9
             float  DS2_T = root["DS18B20-2"]["Temperature"]; // 22.0
             float  DS3_T = root["DS18B20-3"]["Temperature"]; // 21.9
             float  DS4_T = root["DS18B20-4"]["Temperature"]; // 22.6
             float  Temp = root["AM2301"]["Temperature"];     // 22.0
             float  Hum = root["AM2301"]["Humidity"];         // 68.7           
             float  Dist = root["SR04"]["Distance"];          // 40.509          

//        const char* Switch1 = root["Switch1"];        // OFF
//        const char* DS1_ID = root["DS18B20-1"]["Id"]; // 000006F242B1
//        const char* DS2_ID = root["DS18B20-2"]["Id"]; // 000006F2B80A
//        const char* DS3_ID = root["DS18B20-3"]["Id"]; // 00000789E508
//        const char* DS4_ID = root["DS18B20-4"]["Id"]; // 8000001F18D8
//             float  ADS_A0 = root["ADS1115"]["A0"];   // -1
//             float  ADS_A1 = root["ADS1115"]["A1"];   // 26033
//             float  ADS_A2 = root["ADS1115"]["A2"];   // 4851
//             float  ADS_A3 = root["ADS1115"]["A3"];   // 4809
//        const char* TU = root["TempUnit"];// "C"

//        Blynk.virtualWrite(vPIN_DS18B20_1_TEMP,DS1_T);  
//        Blynk.virtualWrite(vPIN_DS18B20_2_TEMP,DS2_T);  
//        Blynk.virtualWrite(vPIN_DS18B20_3_TEMP,DS3_T);  
//        Blynk.virtualWrite(vPIN_DS18B20_4_TEMP,DS4_T);  
//        Blynk.virtualWrite(vPIN_AM2301_TEMP,Temp);  
//        Blynk.virtualWrite(vPIN_AM2301_HUM,Hum);  
//        Blynk.virtualWrite(vPIN_SR04_DIST,Dist);  
        Blynk.virtualWrite(vPIN_ALL_TEMP, (String (DS1_T,1)+"    "+String (DS2_T,1)+"    "+String (DS3_T,1)+"    "+String (DS4_T,1)+"    "+String (Temp,1)));  
        Blynk.setProperty (vPIN_ALL_TEMP, "label",String("\xf0\x9f\x8c\xa1") + "DS18B20-1         DS18B20-2         DS18B20-3         DSPROBE-4         "+String("\xf0\x9f\x8c\xa1")+"AM2302");
        Blynk.virtualWrite(vPIN_HUM,String (Hum,1)+"%"+"  "+String("\xF0\x9F\x93\xB6")+String (rssi)+"%"+"  "+String(Vcc)+"V");
        Blynk.setProperty (vPIN_HUM, "label",String("\xF0\x9F\x92\xA7")+"AM2302  Hum     "+ String (Time)+"     Vcc");

//--------------------------------------------PIR------------------------------------------------
    }else if (String(topic) == InTOPIC_2) { // stairs/PIR_stairs/state = (1/0)
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        if ((char)payload[0] == '1') { // Switch1 on LED if 1 was received as first character
          Blynk.virtualWrite(vPIN_PIR_LED,255);  
          Serial.println("PIR = ON");
          Blynk.setProperty (vPIN_AUTO_MOTION, "label",String("\xF0\x9F\x9A\x85")+ "MOTION  >>> detected");
     }else{
          Blynk.virtualWrite(vPIN_PIR_LED,0);  
          Serial.println("PIR = OFF");
          if (memory2==1) {  
            Blynk.setProperty (vPIN_AUTO_MOTION, "label",String("\xF0\x9F\x92\xAB") +  "    AUTO    PIR            ");
          } else {
            Blynk.setProperty (vPIN_AUTO_MOTION, "label",String("\xE2\x9C\x8B") + "    MANUAL    PIR              ");
          }
     }
//---------------------------------------------POWER1-----------------------------------------------
    }else if (String(topic) == InTOPIC_3) { // stairs/stat/POWER1
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        if ((char)payload[1] == 'N') {
          Blynk.virtualWrite(vPIN_ON_OFF_1,1);  
          Serial.println("POWER1 = ON");
        }else{
          Blynk.virtualWrite(vPIN_ON_OFF_1,0);  
          Serial.println("POWER1 = OFF");
        }
//----------------------------------------------POWER2----------------------------------------------
    }else if (String(topic) == InTOPIC_4) { // stairs/stat/POWER2
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        if ((char)payload[1] == 'N') {
          Blynk.virtualWrite(vPIN_ON_OFF_2,1);  
          Serial.println("POWER2 = ON");
        }else{
          Blynk.virtualWrite(vPIN_ON_OFF_2,0);  
          Serial.println("POWER2 = OFF");
        }
//--------------------------------------------------------------------------------------------
    }else if (String(topic) == InTOPIC_5) { // stairs/Distance
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        Distance = root["Distance"];           
        Blynk.virtualWrite(vPIN_SR04_DIST,Distance);  
        Blynk.setProperty (vPIN_SR04_DIST, "label", "DISTANCE in Cm");
//--------------------------------------------------------------------------------------------
//09:59:13 MQT: stairs/stat/RESULT = {"T1":30,"T2":0,"T3":0,"T4":0,"T5":0,"T6":0,"T7":0,"T8":0}
//10:01:05 MQT: stairs/stat/RESULT = {"POWER1":"ON"}
//10:01:40 MQT: stairs/stat/RESULT = {"POWER1":"OFF"}
//10:04:11 MQT: stairs/stat/RESULT = {"Mem1":"20_"}

    }else if (String(topic) == InTOPIC_6) { //stairs/stat/RESULT
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        int memory1 = root["Mem1"];
        int T1 = root["T1"];
        const char* POWER1 = root["POWER1"]; // "OFF"

        if ( memory1 > 0) {
          Blynk.setProperty(vPIN_PIR_LED, "label",  String("PIR ") + memory1 + String(" S") );
          CountdownRemain =  memory1;
          Serial.print("memory1 = "); Serial.print(memory1); Serial.println(" Sec");
          Serial.println(CountdownRemain);
          if (CountdownRemain) { // check if there is a time set or not
            CountdownShowFormatted(CountdownRemain);
            timer.enable(CountdownTimer);
          }
        }  

        if ( T1 > 0) {
          Blynk.setProperty(vPIN_PIR_LED, "label",  String("PIR ") + T1 + String(" S") );
          CountdownRemain =  T1;
          if (CountdownRemain) { // check if there is a time set or not
            CountdownShowFormatted(CountdownRemain);
            timer.enable(CountdownTimer);
          }
        }  
    }     
  }

timer.h

/*******************************************************************************
 *
 *                                timer.h
 *
 *******************************************************************************/
  void CountdownShowFormatted(int seconds) {
    long hours = 0;
    long mins = 0;
    long secs = 0;
    String secs_o = ":";
    String mins_o = ":";
    secs = seconds; // set the seconds remaining
    mins = secs / 60; //convert seconds to minutes
    hours = mins / 60; //convert minutes to hours
    secs = secs - (mins * 60); //subtract the coverted seconds to minutes in order to display 59 secs max
    mins = mins - (hours * 60); //subtract the coverted minutes to hours in order to display 59 minutes max
    if (secs < 10) {
      secs_o = ":0";
    }
    if (mins < 10) {
      mins_o = ":0";
    }
    if (memory2 == 1){
    Blynk.virtualWrite(vPIN_COUNTDOWN,  String("  ")+ hours + mins_o + mins + secs_o + secs);
    Blynk.setProperty(vPIN_ON_OFF_1, "label", String("\xE2\x8F\xB3") +  String("T1 = ") + hours + mins_o + mins + secs_o + secs + String(" S"));
    } else {
      Blynk.virtualWrite(vPIN_COUNTDOWN, "  0:00:00");
      Blynk.setProperty(vPIN_ON_OFF_1, "label",  String("T1 = ") + "0:00:00" + String(" S") );
    }
  }

  void CountdownTimerFunction() {
    CountdownRemain--;              // remove 1 every second
    CountdownShowFormatted(CountdownRemain);
    if (!CountdownRemain) {   // check if CountdownRemain == 0/FALSE/LOW
      timer.disable(CountdownTimer);     // if 0 stop timer
      if (memory2 == 1) { 
        Blynk.setProperty(vPIN_ON_OFF_1, "label",  String("T1 = ") + "0:00:00" + String(" S") );
        client.publish("stairs/cmnd/POWER1", "0");
      }
    }   
  }

wifi_credentials.h

/*******************************************************************************
 *
 *                          wifi_credentials.h
 *
 ******************************************************************************/

/*******************************************************************************
Local Blynk Server Settings (uncomment to use local server and set address)
*******************************************************************************/

  #define WIFI_SSID          "xxxx"       // ["xxxx"   PW = "xxxx" ]
  #define WIFI_PASS          "xxxx"       //Set pass to "" for open networks.
//#define LOCAL_SERVER       IPAddress(192, 168,xxx, xxx)  //your Local IP Server
  #define Geo_DNS_SERVER     IPAddress(xxx, xxx, xxx, xxx) //FRANKFURT

  • Put all files in the same Folder

all%20files%20in%20same%20folder

2 Likes

“BLYNK” Tasmota Project _# 2

similar to project #1 but with another presentation

HARDWARE

  • 3x DS18B20 Temperature Sensor
  • 1x DHT22 (DHT21, AM2301, AM2302, AM2321)Temperature and Humidity Sensor
  • 1x HC-SR501 PIR Motion Sensor
  • 1x HC-SR04 Ultrasonic Sensor
  • 1x SSD1306 I2C OLED Display
  • 1x PUSH BUTTON (Button1 connected to GPIO0)

AGAIN

before we continue the most important is to send our Rules to Tasmota Console and be sure they are acting as desired.

  1. This project is also Stairs lights (Power1) will turn on if PIR detects a motion [PIR] Mode and lights will remain on for 30 sec. if PIR is triggered again before ruletimer1 ( the countdown timer T1 ) finish the lights will remain on for another 30 sec and so on until there is no more movement .

  2. Changing the [PIR] Mode switch to [TIMER] Mode will stop PIR from detecting motion and set the Timer for 180 sec (or 3 min) that you can change to your needs up to 9999 sec.

  3. A Button to change Temperature from Celsius to Fahrenheit .

  4. Stairs lights can also be controlled by the SR04 ultrasonic sensor (Distance change)

  5. The possibility to change the triggering distance by “Numeric Input Interface” and to stop the US sensor when needed.

  6. Porch lights (Power2) turns OFF with sunrise and ON at sunset.

  7. Porch lights Button will show actual sunrise and sunset time every Hour.

  8. Turn ON / OFF OLED Display (Power3) from Blynk application .

  9. PUSH BUTTON on GPIO0

  • single press : Turn on and off Power1
  • double press : send a mqtt message to toggle Power2
  • hold 2 secs : send another mqtt message to Toggle OLED Display on and off.
  1. Reading of all Temperature and Humidity sensors.
  • In this project Telemetry Period is 300 sec (default) that means sensors will update reading every 5 min. you can change it to 1 min by typing :
    teleperiod 60 in the Console Menu

Last picture is for Console Menu showing Rule3 on one line before sending it to Tasmota

Rule3
on SR04#Distance<%var3% do backlog  publish stairs/Distance {"Distance":%value%};power1 on; ruletimer2 60 endon on rules#timer=2 do power1 off endon
on SR04#Distance>%var3% do backlog  publish stairs/Distance {"Distance":%value%} endon

meaning of Rule3
if ultrasonic distance is less than var3 (variable3) then publish stairs distance value, turn Power1 on,
wait 60 sec until countdown ruletimer2 or T2 finish then turn off Power1.
else if ultrasonic distance is greater than variable3 publish stairs distance value then Blynk can display it in the application.

Power1 is Stairs Lights
Here ruletimer2 is fixed for 60 sec
var3 is the “Numeric Input Distance” set in Blynk app.

Rules are stored in flash and therefore will survive a reboot.

2 Likes



ButtonTopic 0
SetOption1 1
SetOption11 1
SetOption32 20
  
switchmode1 1
Rule2
on Switch1#state=1 do backlog event AutoMotion=%var2%; publish stairs/var2/state %var2% endon
on event#AutoMotion==1 do backlog power1 on; ruletimer1 %var1% endon on rules#timer=1 do power1 off endon
on event#AutoMotion==0 do backlog power1 on; ruletimer1 %var1% endon on rules#timer=1 do power1 off endon
on button1#state=3 do publish stairs/cmnd/power3 2 endon on button1#state=2 do publish stairs/cmnd/power2 2 endon 
 

Rule2 1

Commands Explanation

ButtonTopic 0 : (default) To not use topics for buttons
SetOption1 1 : Allow only single, double and hold press button actions
SetOption11 1 : Swap button single and double press functionality
SetOption32 20 : Set key hold time from 0.1 to 10 seconds (20 = 2 seconds)

SwitchMode1 1 : Will make Switch1#state to be 1 when PIR is ON and 0 when OFF
PIR output is connected to GPIO14 or D5 (Switch1n (82))

Rule2 1 : To enable rule 2

Commands are sent like Rules and are also stored in flash.


BUTTON WITH 3 DIFFERENT ACTIONS

on button1#state=3 do publish stairs/cmnd/power3 2 endon on button1#state=2 do publish stairs/cmnd/power2 2 endon

on button1#state=3 : When holding the button1 for 2 seconds it will toggle Power3 ( OLED on/off)
(state = 3 means HOLD)
on button1#state=2 : When you double press button1 it will toggle Power2 (Porch Lights)
(state = 2 means TOGGLE)
On button1 single press the Light in the Staircase will switch on/off.


Rule1 
on switch1#state do publish stairs/PIR_stairs/state %value% endon
on Time#Set do publish stairs/sunset/state {"Sunset":%sunset%} endon
on Time#Set do publish stairs/sunrise/state {"Sunrise":%sunrise%} endon 
on power3#state do publish stairs/sunset/state {"Sunset":%sunset%} endon
on power3#state do publish stairs/sunrise/state {"Sunrise":%sunrise%} endon

Rule1 1

Rule1 Explanation

SwitchMode1 1

on switch1#state do publish stairs/PIR_stairs/state %value% endon

This mean we will get a 1 if PIR detect a motion and 0 when PIR is in the OFF state.
Tasmota will publish this to Blynk , then Blynk will show a BLUE dot near the PIR label to indicate it is ON.

on Time#Set do publish stairs/sunset/state {"Sunset":%sunset%} endon
on Time#Set do publish stairs/sunrise/state {"Sunrise":%sunrise%} endon 

Here I am using the “TIME” to Trigger the Event but you can use any Trigger from the Rule page.
Tasmota will publish every hour when NTP makes time in sync to Blynk and it will be shown as a label of Power2 ( Porch Lights)

Edited : I add that if you press Power3 OLED display button, Sunrise and Sunset will be updated instantly (you don’t need to wait 60 min to get your update)

Trigger Description Version Introduced
Time#Initialized once when NTP is initialized and time is in sync
Time#Initialized>120 once when NTP is initialized and time is in sync after 02:00 6.1.0
Time#Set every hour when NTP makes time in sync ****
Time#Minute every minute
Time#Minute==241 every day once at 04:01 (241 minutes after midnight) 6.1.0
Time#Minute!5 every five minutes
  • If you live in LONDON and need to adjust your Time + Sunrise & Sunset BST GMT+1
    all what you have to do is to send 3 commands through the Console Menu.
    timezone  1
    Latitude  51.5074
    Longitude  0.1278
  • If you live in PARIS time zone CEST GMT+2 (summer time)
    timezone  2
    Latitude  48.8566
    Longitude  2.3522
  • If you live in MADRID time zone CEST GMT+2 (summer time)
    timezone  2
    Latitude  40.4168
    Longitude  3.7038

also if you need to adjust summer time automatically read Here (TimeSTD
TimeDST)

or

Management

Command Parameters
Timezone -13..13 = set timezone
99 = use timezone configured with TimeDST and TimeSTD
TimeSTD TimeDST Set standard (STD) and daylight saving (DST) timezones
0 = reset timezone parameters to firmware defaults
H , W , M , D , h , T
H = hemisphere ( 0 = northern hemisphere / 1 = southern hemisphere)
W = week ( 0 = last week of month, 1..4 = first … fourth)
M = month ( 1..12 )
D = day of week ( 1..7 1 = sunday 7 = saturday)
h = hour ( 0..23 )
T = timezone ( -780..780 ) (offset from UTC in MINUTES - 780min/60min=13hrs)
Example: TIMEDST 1,1,10,1,2,660

These are Rules you may like to use instead of mine.

Rule to Make Sure Light is on at Night

Using Timers, you can be set to turn on and off a light for illuminate a street/patio by night. But if the
Device has no power at the trigger time, then, when it powers up, the light will be off all night. So, as a failsafe, can be implemented a conditional control to be checked at device Startup with rules.

Set Timers to turn on your light at Sunset and Turn off at sunrise.
Use poweronstate 0 in order to start with lights off when powering up your device.

Set the following rules:

on Time#Initialized do backlog event checksunrise=%time%; event checksunset=%time% endon
on event#checksunset>%sunset% do power1 1 endon
on event#checksunrise<%sunrise% do power1 1 endon

The previous rules are conditionals that represent the following logic:

IF %time%>%sunset% DO Power1 ON also IF %time%<%sunrise% DO Power1 ON


Rule to enable a PIR Sensor only at night

Hardware :

  • PIR HC-SR501
  • GPIO14 09 Switch1 (Sonoff Basic) or Generic Module
  • Set SR501 Jumper ( like this )
  • Latitude and Longitude set in config.h or with Console Menu.

Commands:

SwitchMode1 1

Rule1
on Switch1#state=1 do backlog event checksunrise=%time%; event checksunset=%time% endon
on event#checksunrise<%sunrise% do power1 1 endon
on event#checksunset>%sunset% do power1 1 endon

Rule1 1

All Commands and Rules you must send through Tasmota Console for Blynk to work correctly.

  • Commands:
switchmode1 1

ButtonTopic 0
SetOption1 1
SetOption11 1
SetOption32 20

  • Rules:
Rule1 
on switch1#state do publish stairs/PIR_stairs/state %value% endon
on Time#Set do publish stairs/sunset/state {"Sunset":%sunset%} endon
on Time#Set do publish stairs/sunrise/state {"Sunrise":%sunrise%} endon 



Rule2
on Switch1#state=1 do backlog event AutoMotion=%var2%; publish stairs/var2/state %var2% endon
on event#AutoMotion==1 do backlog power1 on; ruletimer1 %var1% endon on rules#timer=1 do power1 off endon
on event#AutoMotion==0 do backlog power1 on; ruletimer1 %var1% endon on rules#timer=1 do power1 off endon
on button1#state=3 do publish stairs/cmnd/power3 2 endon on button1#state=2 do publish stairs/cmnd/power2 2 endon


Rule3
on SR04#Distance<%var3% do backlog  publish stairs/Distance {"Distance":%value%};power1 on; ruletimer2 60 endon on rules#timer=2 do power1 off endon
on SR04#Distance>%var3% do backlog  publish stairs/Distance {"Distance":%value%} endon

To enable Rules 1, 2, 3 we send

Rule1 1
Rule2 1
Rule3 1

to stop a Rule from working we send a “0”

like
Rule3 0

NODE_MQTT_PIR_TEMP_TIMER.ino

  #include <ESP8266WiFi.h>
  #include <ESP8266mDNS.h>
  #include <WiFiUdp.h>
  #include <ArduinoOTA.h>
  #include <PubSubClient.h>
  #include <ArduinoJson.h>
  
  #include <BlynkSimpleEsp8266.h>
//  #define BLYNK_PRINT Serial    // Comment this to disable prints and save space  

  #include "Settings.h"
  #include "mqtt_publish.h"
  #include "mqtt_subscribe.h"    
  #include "wifi_credentials.h"

/*-------------------------------- Setup -------------------------------------*/
  void setup() {
    WiFi.mode(WIFI_STA);
    Serial.begin(115200);  // See the connection status in Serial Monitor
    Serial.println(F("\n Setup STARTING"));
    WiFi.begin(WIFI_SSID, WIFI_PASS);
    if(WiFi.status() == 6) Serial.println("\tWiFi not connected yet.");

    client.setServer(MQTT_SERVER, 1883);
    client.setCallback(callback);
//----------------------------------Blynk------------------------------------
    #ifdef LOCAL_SERVER
      Blynk.config(AUTH, LOCAL_SERVER ,8080);
    #else
      Blynk.config(AUTH, Geo_DNS_SERVER);
    #endif
    while (Blynk.connect() == false) {             // Wait until connected
      if ((millis() - last_UP_change) > 30000) {   // 30s before reset
        Serial.println(F("resetting"));
        ESP.reset();
      } 
    } 
//-----------------------------------OTA--------------------------------------
    ArduinoOTA.setHostname(OTA_HOSTNAME);
//  ArduinoOTA.setPassword(OTA_PASSWORD);  //If you need a Password uncomment this line
    ArduinoOTA.begin();

    Serial.println(F(" Entering loop"));
    timer.setInterval(4000L, Heartbeat);       // Heartbeat 4000L

    Blynk.syncVirtual(vPIN_ON_OFF_1);
    Blynk.syncVirtual(vPIN_ON_OFF_2);
    Blynk.syncVirtual(vPIN_ON_OFF_3);
    Blynk.syncVirtual(vPIN_PIR_TIMER);        // var2
    Blynk.syncVirtual(vPIN_NUMERIC_TIMER);    // var1
    Blynk.syncVirtual(vPIN_NUMERIC_DISTANCE); // var3

    Blynk.setProperty(vPIN_HEARTBEAT, "label",  String("\xF0\x9F\x92\x93") + " HEARTBEAT");
  }

/*----------------------------------Loop--------------------------------------*/

  void loop() {
    Blynk.run();
    if(!Blynk.connected()) {
      Serial.println(F("Resetting in loop"));
      ESP.restart();
    }
    timer.run();
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
    ArduinoOTA.handle();
  }

/*============================================================================*/

Settings.h

/*****************************************************************************
 *
 *                             Settings.h
 *
 *****************************************************************************/
  BlynkTimer timer;


/*---------------------------Blynk Auth Code----------------------------------*/
  
#define AUTH   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 

 /*-------------------------Over The Air Hostname------------------------------*/

  #define OTA_HOSTNAME          "N-MQTT_Bridge"   // put here your host name
  #define OTA_PASSWORD          "blynk"     // your password for OTA if needed

/*----------------------------Hardware Pins-----------------------------------*/
                             /*-NodeMCU#*/
 

/*-------------------------------Virtual Pins---------------------------------*/
   
  #define vPIN_HEARTBEAT          V0

  #define vPIN_SR04_DIST          V8
  #define vPIN_TEMP_UNIT          V9

  #define vPIN_ALL_TEMP           V10

  #define vPIN_ON_OFF_1           V21     // ON/OFF POWER 1  
  #define vPIN_PIR_TIMER          V22     // select PIR motion or TIMER 
  #define vPIN_NUMERIC_TIMER      V23     // up to 9999 sec
  #define vPIN_AUTO_SR04          V25     // Activate/Desactivate Rule3
  #define vPIN_TIMER_START        V26     // PB START Timer
  #define vPIN_NUMERIC_DISTANCE   V27     // up to 300 cm
  
  #define vPIN_ON_OFF_2           V31     // ON/OFF POWER 2  Activated by Sun  

  #define vPIN_ON_OFF_3           V41     // ON/OFF POWER 3  Turn ON/OFF I2C OLED

/*-----------------------------variables-------------------------------------*/

  bool  var2 ;
  int   var1 ;
  int   var3 ;
  int   counter = 0;
  long int  last_UP_change = millis();
  
  int   rssi;
  float Vcc;
  float Distance;
  String SUNRISE;
  String SUNSET;

/*---------------------------------Heartbeat--------------------------------*/

  void Heartbeat() {
    Blynk.virtualWrite(vPIN_HEARTBEAT,"    \xF0\x9F\x92\x96"); //  
timer.setTimeout(2000L,[](){Blynk.virtualWrite(vPIN_HEARTBEAT,"    \xF0\x9F\x92\x97");}); 
    }

/*--------------------------------------------------------------------------*/

mqtt_publish.h

/******************************************************************************
 *
 *                            mqtt_publish.h
 *
 *****************************************************************************/
  WiFiClient espClient;
  PubSubClient client(espClient);

//----------------------------------------------℃/℉----------------------------------------
  BLYNK_WRITE(vPIN_TEMP_UNIT) {  // Change Temp. Unit from Celsius ℃ to Fahrenheit ℉ 
    if (param.asInt()) {
      client.publish("stairs/cmnd/setoption8","0"); // Celcius ℃
      delay(4000);
      client.publish("stairs/cmnd/teleperiod", "300");  // to get sensor & state info 
      Blynk.setProperty(vPIN_TEMP_UNIT,"onLabel" ,String("\xf0\x9f\x8c\xa1") + "  Celsius ℃ ");
      Blynk.setProperty(vPIN_TEMP_UNIT,"onColor","#FFFFFF");      // White
      Blynk.setProperty(vPIN_TEMP_UNIT,"onBackColor","#8a2be2");  // Blue Violet     
      Blynk.setProperty (vPIN_ALL_TEMP,"color","#8a2be2");        // Blue Violet
    } else {
      client.publish("stairs/cmnd/setoption8","1"); // Fahrenheit ℉
      delay(4000);
      client.publish("stairs/cmnd/teleperiod", "300");   // to get sensor & state info immediately
      Blynk.setProperty(vPIN_TEMP_UNIT,"offLabel",String("\xf0\x9f\x8c\xa1") + "  Fahrenheit ℉ ");
      Blynk.setProperty(vPIN_TEMP_UNIT,"offColor","#04C0F8");     // BLYNK_BLUE
      Blynk.setProperty(vPIN_TEMP_UNIT,"offBackColor","#04C0F8"); // BLYNK_BLUE      
      Blynk.setProperty (vPIN_ALL_TEMP,"color","#04C0F8");        // BLYNK_BLUE
    }
  }
//--------------------------------------------POWER1----(ON/OFF)------------------------------
  BLYNK_WRITE(vPIN_ON_OFF_1) {    // ON/OFF switch
    if (param.asInt()) {
      client.publish("stairs/cmnd/POWER1","1");
    } else {
      client.publish("stairs/cmnd/POWER1","0");
    }
  }
//---------------------------------------------POWER1----(PIR & Timer)------------------------
  BLYNK_WRITE(vPIN_PIR_TIMER) {  // PIR/TIMER
    if (param.asInt()) {
      client.publish("stairs/cmnd/switchmode1","1");
      client.publish("stairs/cmnd/var1","30");
      client.publish("stairs/cmnd/var2","1");
      var1 = 30;
      Blynk.setProperty(vPIN_PIR_TIMER,"label","    PIR        " ); 
      Blynk.setProperty(vPIN_PIR_TIMER,"onLabel",String("\xF0\x9F\x8F\x83"));  
      Blynk.setProperty(vPIN_PIR_TIMER,"onColor","#FFFFFF");       // White
      Blynk.setProperty(vPIN_PIR_TIMER,"onBackColor","#04C0F8");   // BLYNK_BLUE  
      Blynk.setProperty(vPIN_NUMERIC_TIMER,"color","#04C0F8");     // BLYNK_BLUE    
      Blynk.virtualWrite(vPIN_NUMERIC_TIMER,var1);
      Blynk.setProperty(vPIN_NUMERIC_TIMER,"label",String("\xE2\x8F\xB3")+" TIMER "+ 30 +String(" S"));
      Blynk.setProperty(vPIN_TIMER_START,"label","           " );
      Blynk.setProperty(vPIN_TIMER_START,"offLabel","     ");
      Blynk.setProperty(vPIN_TIMER_START,"offColor","#FFFFFF");     // White
      Blynk.setProperty(vPIN_TIMER_START,"offBackColor","#FFFFFF"); // White  
      var2 = 1;
    } else {
      client.publish("stairs/cmnd/switchmode1","0");
      client.publish("stairs/cmnd/var1","180");
      client.publish("stairs/cmnd/var2","0");
      var1 = 180;
      Blynk.setProperty(vPIN_PIR_TIMER,"label",String("\xE2\x8F\xB0") + "    TIMER");
      Blynk.setProperty(vPIN_PIR_TIMER,"offLabel",String("\xE2\x8F\xB0"));
      Blynk.setProperty(vPIN_PIR_TIMER,"offColor","#FFFF00");     // YELLOW
      Blynk.setProperty(vPIN_PIR_TIMER,"offBackColor","#00FF00"); // GREEN  
      Blynk.setProperty(vPIN_NUMERIC_TIMER,"color","#23C48E");    // BLYNK_GREEN
      Blynk.virtualWrite(vPIN_NUMERIC_TIMER,var1);
      Blynk.setProperty(vPIN_NUMERIC_TIMER,"label",String("\xE2\x8F\xB3")+" TIMER "+ 180 +String(" S"));
      Blynk.setProperty(vPIN_TIMER_START,"label","   Start PB" );
      Blynk.setProperty(vPIN_TIMER_START,"offLabel","Start");
      Blynk.setProperty(vPIN_TIMER_START,"offColor","#00FF00");     // GREEN
      Blynk.setProperty(vPIN_TIMER_START,"offBackColor","#00FF00"); // GREEN  
      var2 = 0;
    }
    char value[4];
    dtostrf(var1, 2, 0, value);  
      client.publish("stairs/cmnd/ruletimer1",value);
      BLYNK_WRITE(vPIN_NUMERIC_TIMER);
  }

  BLYNK_WRITE(vPIN_NUMERIC_TIMER) { // TIMER 0-9999 Sec  
   int VALUE = param.asInt();
    var1 = VALUE;
    char value[4];
    dtostrf(VALUE, 2, 0, value);
    client.publish("stairs/cmnd/var1", value);
    client.publish("stairs/cmnd/ruletimer1", value);
    Blynk.virtualWrite(vPIN_NUMERIC_TIMER,var1);
    Blynk.setProperty(vPIN_NUMERIC_TIMER,"label",String("\xE2\x8F\xB3")+" TIMER "+ var1 +String(" S"));
    if (var2 == 1) { 
      client.publish("stairs/cmnd/POWER1", "ON");
    }
  }

  BLYNK_WRITE(vPIN_TIMER_START) { // PB TIMER Sart 
    if (param.asInt()) {
      if (var2 == 0){
        char value[4];
        dtostrf(var1, 2, 0, value);  
        client.publish("stairs/cmnd/ruletimer1",value);
        client.publish("stairs/cmnd/POWER1","1");
      } else {
        client.publish("stairs/cmnd/POWER1","0");
      }
    } 
  }
//---------------------------------------------POWER2----(Sun)------------------------------
  BLYNK_WRITE(vPIN_ON_OFF_2) {    // ON/OFF PORCH Light
    if (param.asInt()) {
      client.publish("stairs/cmnd/POWER2","1");
    } else {
      client.publish("stairs/cmnd/POWER2","0");
      Blynk.setProperty(vPIN_ON_OFF_2,"offColor","#8a2be2");     // Blue Violet
      Blynk.setProperty(vPIN_ON_OFF_2,"offBackColor","#ffff00"); // YELLOW
    }
  }
//---------------------------------------------POWER3----(I2C  OLED)----------------------
  BLYNK_WRITE(vPIN_ON_OFF_3) {    // ON/OFF I2C OLED
    if (param.asInt()) {
      client.publish("stairs/cmnd/POWER3","1");
    } else {
      client.publish("stairs/cmnd/POWER3","0");
    }
  }
//-----------------------------------------------SR04----------------------------------------
  BLYNK_WRITE(vPIN_AUTO_SR04) {  // AUTO/Manual
    if (param.asInt()) {
      client.publish("stairs/cmnd/Rule3","1");
      Blynk.setProperty(vPIN_AUTO_SR04, "label",String("\xF0\x9F\x92\xAB") + "    AUTO    SR04");
    } else {
      client.publish("stairs/cmnd/Rule3","0");
      client.publish("stairs/cmnd/POWER1","0");
      Blynk.virtualWrite(vPIN_SR04_DIST,"--.--");
      Blynk.setProperty (vPIN_AUTO_SR04, "label",String("\xE2\x9C\x8B") + "    MANUAL    SR04");
    }
  }

  BLYNK_WRITE(vPIN_NUMERIC_DISTANCE) { //  
   int VALUE = param.asInt();
    var3 = VALUE;
    char value[4];
    dtostrf(VALUE, 2, 0, value);
    client.publish("stairs/cmnd/var3", value);
  }
//------------------------------------------------------------------------------------------

mqtt_subscribe.h

/*******************************************************************************
 *
 *                            mqtt_subscribe.h
 *
 ******************************************************************************/
  #define MQTT_SERVER        IPAddress(192,168,xxx,xxx) // Your MQTT Broker IP address
  #define MQTT_USERNAME      "xxxx"   // put here your MQTT username
  #define MQTT_PASSWORD      "xxxx"    // put here your MQTT password
  
  #define InTOPIC_0          "stairs/tele/STATE" 
  #define InTOPIC_1          "stairs/tele/SENSOR" 
  #define InTOPIC_2          "stairs/PIR_stairs/state" 
  #define InTOPIC_3          "stairs/stat/POWER1" 
  #define InTOPIC_4          "stairs/stat/POWER2" 
  #define InTOPIC_5          "stairs/stat/POWER3" 
  #define InTOPIC_6          "stairs/stat/RESULT" 
  #define InTOPIC_7          "stairs/sunset/state" 
  #define InTOPIC_8          "stairs/sunrise/state" 
  #define InTOPIC_9          "stairs/Distance" 

  void reconnect() {      // Loop until we're reconnected
    while (!client.connected()) {
      Serial.print(" Attempting MQTT connection...");   // Attempt to connect
      delay(4000);
      if (client.connect("DEVS", MQTT_USERNAME, MQTT_PASSWORD)) {
        Serial.println("connected");
        counter = 0;     // ... and resubscribe
        client.subscribe(InTOPIC_0);
        client.subscribe(InTOPIC_1);
        client.subscribe(InTOPIC_2);
        client.subscribe(InTOPIC_3);
        client.subscribe(InTOPIC_4);
        client.subscribe(InTOPIC_5);
        client.subscribe(InTOPIC_6);
        client.subscribe(InTOPIC_7);
        client.subscribe(InTOPIC_8);
        client.subscribe(InTOPIC_9);
      } else {
        Serial.print("failed, rc=");
        Serial.print(client.state());
        Serial.println(" try again in 4 second");
        ++counter;
        if (counter > 20) ESP.restart();     // Wait 4 seconds before retrying
      }
    }
  }

  void callback(char* topic, byte* payload, unsigned int length) { 
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++) {
      Serial.print((char)payload[i]);
    }
    Serial.println();
//-------------------------------------------STATE--------------------------------------------
//  MQT: stairs/tele/STATE = 
//  {"Time":"2019-03-18T17:56:18","Uptime":"0T18:23:11","Vcc":3.002,"SleepMode":"Dynamic",
//  "Sleep":50,"LoadAvg":19,"POWER1":"ON","POWER2":"OFF","Wifi":{"AP":1,"SSId":"BLYNK",
//  "BSSId":"48:F8:B3:84:AB:22","Channel":10,"RSSI":84,"LinkCount":6,"Downtime":"0T00:00:21"}}

    if (String(topic) == InTOPIC_0) {   // stairs/tele/STATE
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
                   Vcc = root["Vcc"];            // 3.002
                  rssi = root["Wifi"]["RSSI"];   // 84
    const char* Uptime = root["Uptime"];         // 0T18:23:11

//        const char* Time = root["Time"];          // 2019-03-18T17:56:18
//        const char* SleepMode = root["SleepMode"];// Dynamic
//               int  Sleep = root["Sleep"];        // 50
//               int  LoadAvg = root["LoadAvg"];    // 19
//        const char* POWER1 = root["POWER1"];      // ON
//        const char* POWER2 = root["POWER2"];      // OFF
//               int  AP = root["Wifi"]["AP"];      // 1  
//        const char* SSId = root["Wifi"]["SSId"];  // BLYNK
//        const char* BSSId = root["Wifi"]["BSSId"];// 48:F8:B3:84:AB:22
//               int  Chl = root["Wifi"]["Channel"];// 10  
//               int  LC= root["Wifi"]["LinkCount"];// 6
//        const char* DT = root["Wifi"]["Downtime"];// 0T00:00:21

        Blynk.setProperty(vPIN_HEARTBEAT, "label",String("\xE2\x8F\xB3")+" "+String(Uptime));
//-------------------------------------------SENSOR-------------------------------------------
//MQT: stairs/tele/SENSOR = 
//{"Time":"2019-03-19T13:19:16","Switch1":"OFF","DS18B20-1":{"Id":"000006F242B1","Temperature":21.9},
//"DS18B20-2":{"Id":"000006F2B80A","Temperature":22.0},"DS18B20-3":{"Id":"00000789E508","Temperature":21.9},
//"DS18B20-4":{"Id":"8000001F18D8","Temperature":22.6},"AM2301":{"Temperature":22.0,"Humidity":68.7},
//"ADS1115":[{"A0":-1,"A1":26033,"A2":4851,"A3":4809}],"SR04":{"Distance":40.509},"TempUnit":"C"}

    }else if (String(topic) == InTOPIC_1) { // stairs/tele/SENSOR
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        const char* Time = root["Time"];                      // 2019-03-19T13:19:16
             float  DS1_T = root["DS18B20-1"]["Temperature"]; // 21.9
             float  DS2_T = root["DS18B20-2"]["Temperature"]; // 22.0
             float  DS3_T = root["DS18B20-3"]["Temperature"]; // 21.9
             float  DS4_T = root["DS18B20-4"]["Temperature"]; // 22.6
             float  Temp = root["AM2301"]["Temperature"];     // 22.0
             float  Hum = root["AM2301"]["Humidity"];         // 68.7           
             float  Dist = root["SR04"]["Distance"];          // 40.509          

//        const char* Switch1 = root["Switch1"];        // OFF
//        const char* DS1_ID = root["DS18B20-1"]["Id"]; // 000006F242B1
//        const char* DS2_ID = root["DS18B20-2"]["Id"]; // 000006F2B80A
//        const char* DS3_ID = root["DS18B20-3"]["Id"]; // 00000789E508
//        const char* DS4_ID = root["DS18B20-4"]["Id"]; // 8000001F18D8
//             float  ADS_A0 = root["ADS1115"]["A0"];   // -1
//             float  ADS_A1 = root["ADS1115"]["A1"];   // 26033
//             float  ADS_A2 = root["ADS1115"]["A2"];   // 4851
//             float  ADS_A3 = root["ADS1115"]["A3"];   // 4809
//        const char* TU = root["TempUnit"];// "C"


        Blynk.setProperty(vPIN_TEMP_UNIT, "label",String("\xE2\x8C\x9A")+Time+"   "+String("\xF0\x9F\x93\xB6")+" WiFi = "+rssi+" %"+"   "+String("\xE2\x9A\xA1")+"Vcc = "+String(Vcc,2)+" V" );

        Blynk.virtualWrite(vPIN_ALL_TEMP, (String (DS1_T,1)+"    "+String (DS2_T,1)+"    "+String (DS3_T,1)+"    "+String (Temp,1)+"    "+String(Hum,1)+"%"));  
        Blynk.setProperty (vPIN_ALL_TEMP, "label",String("\xf0\x9f\x8c\xa1")+"DS18B20-1      DS18B20-2      DSPROBE-3      "+String("\xf0\x9f\x8c\xa1")+"AM2302"+"         "+String("\xF0\x9F\x92\xA7")+"AM2302");

//--------------------------------------------PIR---------------------------------------------
    }else if (String(topic) == InTOPIC_2) { // stairs/PIR_stairs/state = (1/0)
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        if ((char)payload[0] == '1') { // Switch1 on LED if 1 was received as first character
          Serial.println("PIR = ON");
          Blynk.setProperty(vPIN_PIR_TIMER,"label","    PIR      " +String("\xF0\x9F\x94\xB5"));
        }
        if ((char)payload[0] == '0'){
          Serial.println("PIR = OFF");
          if (var2==1) {  
            Blynk.setProperty(vPIN_PIR_TIMER,"label", "    PIR          ");
            Blynk.setProperty(vPIN_PIR_TIMER,"onLabel",String("\xF0\x9F\x8F\x83"));
            Blynk.setProperty(vPIN_PIR_TIMER,"onColor","#FFFFFF");       // White
            Blynk.setProperty(vPIN_PIR_TIMER,"onBackColor","#04C0F8");   // BLYNK_BLUE  
          } 
          if (var2==0)  {
            Blynk.setProperty(vPIN_PIR_TIMER,"label",String("\xE2\x8F\xB0") + "    TIMER             ");
            Blynk.setProperty(vPIN_PIR_TIMER,"offLabel",String("\xE2\x8F\xB0"));
            Blynk.setProperty(vPIN_PIR_TIMER,"offColor","#E0E000");      // dark YELLOW
            Blynk.setProperty(vPIN_PIR_TIMER,"offBackColor","#23C48E");  // BLYNK_GREEN
          }
        }
//---------------------------------------------POWER1-----------------------------------------
    }else if (String(topic) == InTOPIC_3) { // stairs/stat/POWER1
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        if ((char)payload[1] == 'N') {
          Blynk.virtualWrite(vPIN_ON_OFF_1,1);  
          Serial.println("POWER1 = ON");
        }else{
          Blynk.virtualWrite(vPIN_ON_OFF_1,0);  
          Serial.println("POWER1 = OFF");
        }
//---------------------------------------------POWER2-----------------------------------------
    }else if (String(topic) == InTOPIC_4) { // stairs/stat/POWER2
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        if ((char)payload[1] == 'N') {
          Blynk.virtualWrite(vPIN_ON_OFF_2,1);  
          Serial.println("POWER2 = ON");
        }else{
          Blynk.virtualWrite(vPIN_ON_OFF_2,0);  
          Serial.println("POWER2 = OFF");
        }
//---------------------------------------------POWER3-----------------------------------------
    }else if (String(topic) == InTOPIC_5) { // stairs/stat/POWER3
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        if ((char)payload[1] == 'N') {
          Blynk.virtualWrite(vPIN_ON_OFF_3,1);  
          Serial.println("POWER3 = ON");
        }else{
          Blynk.virtualWrite(vPIN_ON_OFF_3,0);  
          Serial.println("POWER3 = OFF");
        }
//--------------------------------------------------------------------------------------------
//09:59:13 MQT: stairs/stat/RESULT = {"T1":20,"T2":0,"T3":0,"T4":0,"T5":0,"T6":0,"T7":0,"T8":0}
//10:01:05 MQT: stairs/stat/RESULT = {"POWER1":"ON"}
//10:01:40 MQT: stairs/stat/RESULT = {"POWER1":"OFF"}
//10:04:11 MQT: stairs/stat/RESULT = {"var1":"20"}

    }else if (String(topic) == InTOPIC_6) { //stairs/stat/RESULT
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        int var1 = root["var1"];
        int T1 = root["T1"];
        const char* POWER1 = root["POWER1"]; // "OFF"
//------------------------------------------------sunset--------------------------------------
    }else if (String(topic) == InTOPIC_7) { //stairs/sunset/state
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        int mins = root["Sunset"];
        int hours = mins / 60;      //convert minutes to hours
        mins = mins - (hours * 60); //subtract the coverted minutes to hours in order to display 59 minutes max
        String mins_o = ":";
        if (mins < 10) { mins_o = ":0";}
        SUNSET = hours + mins_o + mins ;
//------------------------------------------------sunrise-------------------------------------
    }else if (String(topic) == InTOPIC_8) { //stairs/sunrise/state
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        int mins = root["Sunrise"];
        int hours = mins / 60;      //convert minutes to hours
        mins = mins - (hours * 60); //subtract the coverted minutes to hours in order to display 59 minutes max
        String mins_o = ":";
        if (mins < 10) { mins_o = ":0";}
        SUNRISE = hours + mins_o + mins ;
        // every Hour update Sunrise & Sunset [Tasmota => " on Time#Set do publish "]
        Blynk.setProperty(vPIN_ON_OFF_2,"label"," [* PORCH    LIGHT *]            Sunrise @ "+SUNRISE + "            "+"Sunset @ "+SUNSET);
        Serial.print("SUNRISE @ ");Serial.print(SUNRISE);Serial.print("    ");Serial.print("SUNSET1 @ "); Serial.println(SUNSET);
//-------------------------------------------------SR04---------------------------------------
    }else if (String(topic) == InTOPIC_9) { // stairs/Distance
      StaticJsonBuffer<450> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject((char*)payload);
        Distance = root["Distance"];           
        Blynk.virtualWrite(vPIN_SR04_DIST,Distance);  
        Blynk.setProperty (vPIN_SR04_DIST, "label", "DISTANCE in Cm");
    }
  }
//--------------------------------------------------------------------------------------------



wifi_credentials.h

/*******************************************************************************
 *
 *                          wifi_credentials.h
 *
 ******************************************************************************/

/*******************************************************************************
Local Blynk Server Settings (uncomment to use local server and set address)
*******************************************************************************/

  #define WIFI_SSID          "xxxx"       // ["xxxx"   PW = "xxxx" ]
  #define WIFI_PASS          "xxxx"       //Set pass to "" for open networks.
//#define LOCAL_SERVER       IPAddress(192, 168,xxx, xxx)  //your Local IP Server
  #define Geo_DNS_SERVER     IPAddress(xxx, xxx, xxx, xxx) //FRANKFURT
  • Displays

1. SSD1306 OLED 128x32/128x64 (default addresses 0x3C , 0x3D )

First for our OLED to work you must uncomment the oled section in TASMOTA
my_user_config.h
before Building and Uploading

 #define USE_DISPLAY                    // Add I2C Display Support (+2k code)
    #define USE_DISPLAY_MODES1TO5       // Enable display mode 1 to 5 in addition to mode 0
    #define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code)
    #define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code)
    #define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code)
      #define MTX_ADDRESS1     0x71 // [DisplayAddress1] I2C address of first 8x8 matrix module
      #define MTX_ADDRESS2     0x74 // [DisplayAddress2] I2C address of second 8x8 matrix module
      #define MTX_ADDRESS3     0x75 // [DisplayAddress3] I2C address of third 8x8 matrix module
      #define MTX_ADDRESS4     0x72 // [DisplayAddress4] I2C address of fourth 8x8 matrix module
      #define MTX_ADDRESS5     0x73 // [DisplayAddress5] I2C address of fifth 8x8 matrix module
      #define MTX_ADDRESS6     0x76 // [DisplayAddress6] I2C address of sixth 8x8 matrix module
      #define MTX_ADDRESS7     0x00 //[DisplayAddress7] I2C address of seventh 8x8 matrix module
      #define MTX_ADDRESS8     0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module
#endif  // USE_I2C




My OLED Display settings

Backlog  DisplayMode 2; DisplayModel 2; DisplayCols 22; DisplayRows 8

02:24:07 CMD: Display
02:24:07 MQT: stairs/stat/RESULT = {"Display":{"Model":2,"Mode":2,"Dimmer":1,"Size":1,"Font":1,"Rotate":0,"Refresh":2,"Cols":[22,8],"Rows":8}}

Displays

Command Parameters
Display Show current display setting as JSON string
DisplayAddress 0..255 = set display module address
DisplayDimmer 0 = switch the display off
1..100 = switch the display on
0..100 = set display luminosity (only on 8x8 Dot-Matrix displays)
DisplayMode 0..5 = set to display predefined content according to display type
DisplayModel Set model of your display:
1 = I2C LCD Display (default addresses 0x27 , 0x3F )
2 = SSD1306 OLED 128x32/128x64 (default addresses 0x3C , 0x3D )
3 = 8x8 Dot-Matrix
4 = ILI9341 TFT LCD
5 = E-Paper Display
DisplayRefresh 1..7 = set time in seconds to update predefined content when using DisplayMode0
DisplaySize 1..4 = set display scale-up size (only for DisplayModel 2 (SSD1306 OLED) and DisplayModel 4 (ILI9341 TFT LCD))
DisplayRotate Set rotation angle
0 = 0°
1 = 90°
2 = 180°
3 = 270°
DisplayText <value> = for a full guide see DisplayText use
DisplayCols 1..44 = set number of display columns
DisplayRows 1..32 = set number of display rows
DisplayFont (reserved command, currently unsupported)

DisplayMode Parameters

The display driver is able to display predefined setups of text or user defined text. To display text using DisplayText set DisplayMode to 0 (also 1 for DisplayModel 3 (Dot-Matrix)).

Parameter LCD Display OLED Display TFT Display
0 DisplayText DisplayText DisplayText
1 Time/Date Time/Date Time/Date
2 Local sensors Local sensors Local sensors
3 MQTT and Time/Date Local sensors and Time/Date Local sensors and Time/Date
4 Local sensors MQTT and local sensors MQTT and local sensors
5 MQTT and Time/Date MQTT, local sensors and Time/Date MQTT, local sensors and Time/Date
  1. I2C LCD Display (default addresses 0x27 , 0x3F )

If LCD is enabled in TASMOTA
my_user_config.h
and you don’t see any character on your screen.
check the DisplayAddress for 0x27 or 0x3F if it is not, you can correct it by sending the correct address.

Command : DisplayAddress 0x3F in HEX or DisplayAddress 63 in DECIMAL (both works)

My LCD Display settings

11:18:39 CMD: display
11:18:39 MQT: stairs/stat/RESULT = {"Display":{"Model":1,"Mode":1,"Dimmer":1,"Size":1,"Font":1,"Rotate":0,"Refresh":2,"Cols":[16,8],"Rows":2}}


  • to see all DEVICES connected to your “I2C

Command: I2CScan


More DISPLAYS ca be Found HERE

DisplayText Use

The functionality described here is expected to be merged from PR 3748

The DisplayText command is used to display text as well as graphics and graphs on LCD, OLED and e-Paper displays. The command argument is a string that is printed on the display at the current position. The string can be prefixed by embedded control commands enclosed in brackets [] .

In order to use the DisplayText command the DisplayMode must be set to 0 (or optional 1 on LCD displays) or other modes must be disabled before compilation with #undef USE_DISPLAY_MODES1TO5 .

DisplayText parameters

In the list below _p_ stands for parameter and may be a number from 1 to n digits. On monochrome graphic displays things are drawn into a local frame buffer and sent to the display either via the d command or automatically at the end of the command.

Positioning

l _p_ = sets a character line to print at (on LCD display p = {0…}, on TFT display p = {1…})
c _p_ = sets a character column to print at (on LCD display p = {0…}, on TFT display p = {1…})
x _p_ = sets the x position for consecutive prints
y _p_ = sets the y position for consecutive prints

Text is printed at the last provided position, either l or y for the vertical position, and either x or x for the horizontal position. Neither x nor y are advanced/updated after printing text.

Line primitives

h _p_ = draws a horizontal line with length p (x is advanced)
v _p_ = draws a vertical line with length p (y is advanced)
L _p_:_p_ = draws a line to p : p (x,y are advanced)
k _p_ = draws a circle with radius p
K _p_ = draws a filled circle with radius p
r _p_:_p_ = draws a rectangle with p with and p height
R _p_:_p_ = draws a filled rectangle with p with and p height

Miscellaneous

z = clear the display
i = (re)init the display (in e-Paper mode with partial update)
I = (re)init the display (in e-Paper mode with full update)
d = update the display
D _p_ = switch display auto updates on( p =1)/off( p =0), when off display must be updated with d
o = switch display off
O = switch display on
t = display Tasmota time in HH:MM
T = display Tasmota date in DD.MM.YY
s _p_ = set text scaling for classic GFX font (scaling factor 1…N)
f _p_ = set font (1=12, 2=24,(opt 3=8)) if font==0 the classic GFX font is used
C _p_ = set color (0,1) for black or white (later for color displays index colors)

Notes about e-Paper displays

E-Paper displays have 2 operating modes: full update and partial update. While full update delivers a clean and sharp picture it has the disadvantage of taking several seconds for the screen update and shows severe flickering during update. Partial update is quite fast (300 ms) with no flickering but there is the possibility that erased content is still slightly visible. To “whiten” the display it is therefore useful to perform a full update in regular intervals (e.g each hour).

Defines => USE_SOFTSPI, USE_DISPLAY_EPAPER29, USE_DISPLAY_EPAPER42

Hardware connections:

I2C displays are connected in the usual manner and defined via Tasmota pin selection. The I2C Adress must be given by DisplayAddress XX , e.g. 60, and the model set with DisplayModel ,e.g. 2 for SSD1306. To permanently turn the display on set DisplayDimmer 100 . Display rotation can be permanently set using DisplayRotate X (x = 0..3 ).

E-Paper displays are connected via 3 wire SPI (CS, SCLK, MOSI) the other 3 Interface lines of the display (DC, Reset, busy) may be left unconnected. The jumper on the circuit board of the display must be set to 3 wire SPI.

Examples

Print Text at size 1 on line 1, column 1:

DisplayText [s1l1c1]Hello how are you?

Draw a rectangle and draw text inside with size 2 and 7 chars padded with spaces:

DisplayText [x85y95h130v30h-130v-30s2p7x90y100]37.25 C

Clear screen:

DisplayText [z]

Draw rectangle from x,y with width and height:

DisplayText [x50y50r200:100]

Display local sensors using rules

Show sensor values, time and a separation line, whiten display every 60 minutes (line breaks and indentation added for readability):

rule1 on tele-SHT3X-0x44#Temperature do DisplayText [f1p7x0y5]%value% C endon
      on tele-SHT3X-0x44#Humidity do DisplayText [f1p10x70y5]%value% %[x0y20h296x250y5t] endon
      on tele-BMP280#Pressure do DisplayText [f1p10x140y5]%value% hPa endon
      on Time#Minute|60 do DisplayText [Tt] endon

Show 4 analog channels (line breaks and indentation added for readability):

rule1 on tele-ADS1115#A0 do DisplayText [s1p21c1l01]Analog1: %value% adc endon
      on tele-ADS1115#A1 do DisplayText [s1p21c1l3]Analog2: %value% adc endon
      on tele-ADS1115#A2 do DisplayText [s1p21c1l5]Analog3: %value% adc endon
      on tele-ADS1115#A3 do DisplayText [s1p21c1l7]Analog4: %value% adc endon

Show BME280 + SGP30 (line breaks and indentation added for readability):

rule1 on tele-BME280#Temperature do DisplayText [s1p21x0y0]Temp: %value% C endon
      on tele-BME280#Humidity do DisplayText [s1p21x0y10]Hum : %value% %% endon
      on BME280#Pressure do DisplayText [s1p21x0y20]Prss: %value% hPa endon
      on tele-SGP30#TVOC do DisplayText [s1p21x0y30]TVOC: %value% ppb endon
      on tele-SGP30#eCO2 do DisplayText [s1p21x0y40]eCO2: %value% ppm [s1p0x0y50]Time: [x35y50t] endon

Notes about display drivers:

Waveshare has 2 kinds of display controllers: with partial update and without partial update. The 2.9 inch driver is for partial update and should support also other Waveshare partial update models with modified WIDTH and HEIGHT parameters. The 4.2 inch driver is a hack which makes the full update display behave like a partial update and should probably work with other full update displays.

The drivers are sub classes of the Adafruit GFX library. The class hierarchy is LOWLEVEL :: Paint :: Renderer :: GFX , where: GFX : unmodified Adafruit library Renderer : the interface for Tasmota Paint : the modified pixel driver for e-paper

  • there are several virtual functions that can be subclassed down to LOWLEVEL .

The display dispatcher only does the class init call. All other calls go to the Renderer class.

In black and white displays a local ram buffer must be allocated before calling the driver. This must be set to zero on character or TFT color displays.

To use the 400x300 e-Paper display the Arduino library 2.4 or later must be used because it leaves much more RAM available than prior versions. This display requires 15k of RAM!

About 28 k flash is used by these 4 drivers, the epd fonts use about 9k space, which can be if-def’d.

  • SSD1306 = 1,15 k
  • SH1106 = 1,18 k
  • EPD42 = 2,57 k
  • EPD29 = 2,1 k
  • Display and Render class about 12 k

Connected Power Meter using PZEM-004T, Wemos D1 Mini and a 1602 I2C display

Details are in this link:

2 Likes