Here is the complete code, it’s a heater controller.There may be a lot of room for improvement,please comment!
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <WiFiManager.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h> //ArduinoJson only can use 5.xxx version
#include <FS.h>
#include <Timer.h>
#include <Ticker.h>
#include <ArduinoOTA.h>
//web ota
#include <ESP8266HTTPUpdateServer.h>
#include <ESP8266mDNS.h>
//ds1820
#include <OneWire.h>
#include <DallasTemperature.h>
/* Comment this out to disable prints and save space */
//#define BLYNK_PRINT Serial
#define BLYNK_PRINT Serail
//#define BLYNK_DEBUG
WidgetTerminal terminal(V12);
BlynkTimer on_timer, off_timer, delay_timer;
WiFiManager wifiManager;
//web ota
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;
//flag for saving data
bool shouldSaveConfig = false;
String msg = ""; //string of message
const char *pwrOnStr[] = {
"== == == == == == == == == == == == == =",
"smartHeater V1.9",
"Author:xxx",
"2019-1",
"== == == == == == == == == == == == == ="};
char blynk_token[] = "xxx";
char mqtt_server[40] = "xxx";
char mqtt_port[6] = "8080";
int port = 8080;
//
#define relay D1 // gpio5
#define led D4 // gpio2,low active
#define standby_led D6 //gpio12,停止加热指示
#define button D2 // gpio4,on off physic button
#define srv_led D5 //gpio14, server connected status
int DQ = 13; // gpio13,DS18B20
// define virtual button
#define w_button V2 // wediget on off button
#define w_on_set V1 // on time set wediget
#define w_off_set V0 // off time set wediget
#define w_on_time V4 // display current on time
#define w_off_time V3 // display current off time
#define w_seg_sw V7 //segment switch
#define w_on_delay V8 //显示手动开启后的剩余时间
#define w_msg V9 //显示信息
#define w_title V10 //显示软件名称和版本号
#define w_seg_sw_st V11 //显示三档开关的历史状态
#define w_temp V14 //显示水温
WidgetLED w_on_led(V6);
WidgetLED w_off_led(V5);
#define ON LOW
#define OFF HIGH
#define ONE_MIN 60000UL //miniseconds of one minute
int on_delay_time = 15; // when relay on, delay 15 min to autooff
int on_delay_time_remain = 0; //手动开启后自动关闭的剩余时间
int on_tmr_id;
int on_time = 10; // default on time,10 mins
int off_time = 90; // default off time,90 mins
#define MIN_ONTIME 5
#define MAX_ONTIME 60
#define MIN_OFFTIME 5
#define MAX_OFFTIME 180
int on_id = 0; // 开启继电器的定时器 id
int off_id = 0; // 关闭继电器的定时器 id
int on_delay_id = 0;
int on_time_remain = 0;
int off_time_remain = 0;
#define KEY_DEBOUNCE_TIME 200UL //按键检测去抖动时间,ms
#define KEY_RESET_TIME 5000UL //按键按下5秒后清除wifi配置,ms
#define KEY_OTA_TIME 10000UL //按键按下10秒后进入OTA升级
Ticker ticker; //wifi配置时的LED指示状态
Ticker polling_button; //定时查询按键状态
//定义按键状态
#define KEY_RESET 0
#define KEY_DOWN 1
#define KEY_UP 2
#define KEY_OTA 3
volatile byte keyState = KEY_UP;
unsigned long button_PushdownCnt = 0;
//定义3档开关的状态
#define AUTO 1
#define NORMAL_ON 2
#define NORMAL_OFF 3
volatile byte tri_seg_sw_state = NORMAL_OFF; //记录3档开关状态
bool OTA_FLAG = false; //OTA升级标记
#define ONE_WIRE_BUS DQ
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature DS18B20(&oneWire);
// arrays to hold device address
DeviceAddress insideThermometer;
unsigned long previousMillis = 0;
unsigned long temp_read_interval = 60000; //温度读取间隔
//callback notifying us of the need to save config
void saveConfigCallback()
{
Serial.println("Save Wifi config");
shouldSaveConfig = true;
}
void SaveWifiConfig()
{
//save the custom parameters to FS
DynamicJsonBuffer jsonBuffer;
JsonObject &json = jsonBuffer.createObject();
json["mqtt_server"] = mqtt_server;
json["mqtt_port"] = mqtt_port;
json["ip"] = WiFi.localIP().toString();
File configFile = SPIFFS.open("/wifi.json", "w");
if (!configFile)
{
Serial.println("failed to open config file for writing");
}
json.prettyPrintTo(Serial);
json.printTo(configFile);
configFile.close();
//end save
}
//如果wifi连不上,则进入wifi配置
bool wifi_config()
{
// read configuration from FS json
Serial.println("mounting FS...");
if (SPIFFS.begin())
{
Serial.println("mounted file system");
if (SPIFFS.exists("/wifi.json"))
{
// file exists, reading and loading
Serial.println("reading config file");
File configFile = SPIFFS.open("/wifi.json", "r");
if (configFile)
{
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer;
JsonObject &json = jsonBuffer.parseObject(buf.get());
json.printTo(Serial);
if (json.success())
{
Serial.println("\nparsed json");
strcpy(mqtt_server, json["mqtt_server"]);
strcpy(mqtt_port, json["mqtt_port"]);
// strcpy(blynk_token, json["blynk_token"]);
}
else
{
Serial.println("failed to load json config");
}
configFile.close();
}
}
}
else
{
Serial.println("failed to mount FS");
// return false;
}
// end read
WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5);
// set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
// add all your parameters here
wifiManager.addParameter(&custom_mqtt_server);
wifiManager.addParameter(&custom_mqtt_port);
// wifiManager.addParameter(&custom_blynk_token);
wifiManager.setTimeout(180);
if (!wifiManager.autoConnect()) //use this for auto generated name ESP + ChipID
{
Serial.println("failed to connect and hit timeout");
delay(3000);
// reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(5000);
}
//read updated parameters
strcpy(mqtt_server, custom_mqtt_server.getValue());
strcpy(mqtt_port, custom_mqtt_port.getValue());
if (shouldSaveConfig)
SaveWifiConfig(); //保存wifi配置
Serial.printf("local ip:");
Serial.println(WiFi.localIP().toString());
ticker.detach();
}
void tick()
{
//toggle led state
int state = digitalRead(led); // get the current state of led pin
digitalWrite(led, !state); // set pin to the opposite state
}
BLYNK_WRITE(w_readInterval)
{
temp_read_interval = 1000 * param.asInt();
}
//read ds1820
float pre_temp = 0;
void Read_Temp()
{
DS18B20.requestTemperatures();
float now_temp = DS18B20.getTempC(insideThermometer);
Serial.print("Temp C: ");
Serial.println(now_temp);
Blynk.virtualWrite(w_temp, now_temp);
msg = "Read DS1820";
Blynk.virtualWrite(w_msg, msg);
terminal.print("t=");
terminal.println(now_temp);
terminal.flush();
//温度上升
if ((now_temp - pre_temp) > 0.2 && digitalRead(relay)) //差值大于0.2度表明在加热
{
digitalWrite(standby_led, LOW); //turn off led
}
else if (tri_seg_sw_state == NORMAL_OFF) //常关时熄灭led
{
digitalWrite(standby_led, LOW);
}
else
{
digitalWrite(standby_led, HIGH); //turn on led
}
pre_temp = now_temp;
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16)
Serial.print("0");
Serial.println(deviceAddress[i], HEX);
}
}
//ds1820 init
bool ds1820_init(void)
{
// locate devices on the bus
Serial.print("Locating devices...");
DS18B20.begin();
Serial.print("Found ");
int numDevice = DS18B20.getDeviceCount();
Serial.print(numDevice, DEC);
Serial.println(" devices.");
msg = "find " + String(numDevice) + " devices.";
Blynk.virtualWrite(w_msg, msg);
if (!DS18B20.getAddress(insideThermometer, 0))
{
Serial.println("Unable to find address for Device 0");
return false;
}
else
{
// show the addresses we found on the bus
Serial.print("Device 0 Address: ");
printAddress(insideThermometer);
Serial.println();
// set the resolution to 10 bit (Each Dallas/Maxim device is capable of several different resolutions)
DS18B20.setResolution(insideThermometer, 10);
Serial.print("Device 0 Resolution: ");
Serial.print(DS18B20.getResolution(insideThermometer), DEC);
Serial.println();
//set one min to read
//ds1820_timer.setInterval(ONE_MIN, Read_Temp); // read temperature every time
return true;
}
}
//1ms检查一次按键状态并计数
void buttonPolling()
{
if (!digitalRead(button)) //key down
{
button_PushdownCnt++; //记录按键按下的ms数
}
else if (button_PushdownCnt > 0) //按鍵按下再釋放,則判断按键功能
{
if (button_PushdownCnt >= KEY_OTA_TIME)
{
keyState = KEY_OTA;
}
else if (button_PushdownCnt >= KEY_RESET_TIME)
{
keyState = KEY_RESET;
}
else if (button_PushdownCnt >= KEY_DEBOUNCE_TIME)
{
keyState = KEY_DOWN;
}
else
{
keyState = KEY_UP;
}
button_PushdownCnt = 0;
}
}
void ota_update()
{
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR)
Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR)
Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR)
Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR)
Serial.println("Receive Failed");
else if (error == OTA_END_ERROR)
Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("OTA Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void one_min_on()
{
on_time_remain--;
Blynk.virtualWrite(w_on_time, on_time_remain);
Blynk.virtualWrite(w_off_time, off_time);
Blynk.virtualWrite(w_seg_sw_st, tri_seg_sw_state);
w_on_led.on();
w_off_led.off();
Serial.print("on timer remained:");
Serial.println(on_time_remain);
if (on_time_remain == 0)
{
on_timer.deleteTimer(on_id);
off_id = off_timer.setTimer(ONE_MIN, one_min_off, off_time);
on_time_remain = on_time;
off_time_remain = off_time;
Blynk.virtualWrite(w_on_time, on_time_remain);
w_on_led.off();
w_off_led.on();
digitalWrite(led, OFF);
digitalWrite(relay, LOW);
Blynk.virtualWrite(w_button, digitalRead(relay));
}
}
//off状态下每隔1分钟更新一次widiget的状态
void one_min_off()
{
off_time_remain--;
Blynk.virtualWrite(w_off_time, off_time_remain);
Blynk.virtualWrite(w_on_time, on_time);
Blynk.virtualWrite(w_seg_sw_st, tri_seg_sw_state);
w_off_led.on();
w_on_led.off();
Serial.print("off timer remained:");
Serial.println(off_time_remain);
if (off_time_remain == 0)
{
off_timer.deleteTimer(off_id);
on_id = on_timer.setTimer(ONE_MIN, one_min_on, on_time);
on_time_remain = on_time;
off_time_remain = off_time;
Blynk.virtualWrite(w_off_time, off_time_remain);
w_on_led.on();
w_off_led.off();
digitalWrite(led, ON);
digitalWrite(relay, HIGH);
Blynk.virtualWrite(w_button, digitalRead(relay));
}
}
//更新on timer的剩余时间
BLYNK_READ(w_on_time)
{
Blynk.virtualWrite(w_on_time, on_time_remain);
}
//更新off timer的剩余时间
BLYNK_READ(w_off_time)
{
Blynk.virtualWrite(w_off_time, off_time_remain);
}
//on off widiget按键的处理
BLYNK_WRITE(w_button) // app on off button
{
if (param.asInt() == 1)
{
if (tri_seg_sw_state == AUTO)
{
digitalWrite(relay, HIGH);
digitalWrite(led, ON);
on_delay_time_remain = on_delay_time;
on_delay_id = delay_timer.setTimer(ONE_MIN, AutoturnOff, on_delay_time);
//delete current timer
on_timer.deleteTimer(on_id);
off_timer.deleteTimer(off_id);
off_time_remain = off_time;
Blynk.virtualWrite(w_on_time, on_time);
Blynk.virtualWrite(w_off_time, off_time);
Blynk.virtualWrite(w_on_delay, on_delay_time);
//app上的led全部熄灭
w_on_led.off();
w_off_led.off();
Serial.println("relay is on");
}
else
{
Blynk.virtualWrite(w_button, digitalRead(relay));
if (delay_timer.isEnabled(on_delay_id))
{
delay_timer.deleteTimer(on_delay_id);
}
}
}
else //off
{
if (tri_seg_sw_state == AUTO)
{
on_timer.deleteTimer(on_id);
delay_timer.deleteTimer(on_delay_id);
off_time_remain = off_time;
off_id = off_timer.setTimer(ONE_MIN, one_min_off, off_time);
digitalWrite(relay, LOW);
digitalWrite(led, OFF);
w_off_led.on();
w_on_led.off();
Blynk.virtualWrite(w_on_delay, 0);
Serial.println("relay is off");
}
else
{
Blynk.virtualWrite(w_button, digitalRead(relay));
}
}
}
//once on timer callback function
void AutoturnOff()
{
on_delay_time_remain--;
if (on_delay_time_remain == 0)
{
off_id = off_timer.setTimer(ONE_MIN, one_min_off, off_time); //进入关闭定时循环
delay_timer.deleteTimer(on_delay_id);
digitalWrite(relay, LOW);
digitalWrite(led, OFF);
//初始化定时参数
on_time_remain = on_time;
off_time_remain = off_time;
Blynk.virtualWrite(w_on_time, on_time);
Blynk.virtualWrite(w_off_time, off_time);
Blynk.virtualWrite(w_button, digitalRead(relay));
Blynk.virtualWrite(w_on_delay, on_delay_time_remain);
Serial.println("relay is off");
w_off_led.on(); //debug
}
else
{
Serial.print("on time remained:");
Serial.println(on_delay_time_remain);
Blynk.virtualWrite(w_on_delay, on_delay_time_remain);
}
}
//app 3档开关功能
BLYNK_WRITE(w_seg_sw)
{
tri_seg_sw_state = param.asInt();
doTriSwitch();
}
//根据3档开关的状态执行相应动作
void doTriSwitch()
{
switch (tri_seg_sw_state)
{
case AUTO: //auto on and off
{
if (on_time_remain < on_time)
{
on_id = on_timer.setTimer(ONE_MIN, one_min_on, on_time);
digitalWrite(relay, HIGH);
digitalWrite(led, ON);
w_on_led.on();
}
else
{
off_id = off_timer.setTimer(ONE_MIN, one_min_off, off_time);
digitalWrite(relay, LOW);
digitalWrite(led, OFF);
w_off_led.on();
}
on_time_remain = on_time;
off_time_remain = off_time;
Blynk.virtualWrite(w_on_time, on_time);
Blynk.virtualWrite(w_off_time, off_time);
Blynk.virtualWrite(w_button, digitalRead(relay));
break;
}
case NORMAL_ON: //alway on
{
//app上的led全部熄灭
w_on_led.off();
w_off_led.off();
on_timer.deleteTimer(on_id);
off_timer.deleteTimer(off_id);
delay_timer.deleteTimer(on_delay_id);
digitalWrite(led, ON);
digitalWrite(relay, HIGH);
Serial.println("relay is on");
tri_seg_sw_state = NORMAL_ON;
Blynk.virtualWrite(w_on_delay, 0);
Blynk.virtualWrite(w_seg_sw_st, tri_seg_sw_state);
Blynk.virtualWrite(w_button, digitalRead(relay));
break;
}
case NORMAL_OFF: //alway off
{
//app上的led全部熄灭
w_on_led.off();
w_off_led.off();
on_timer.deleteTimer(on_id);
off_timer.deleteTimer(off_id);
delay_timer.deleteTimer(on_delay_id);
digitalWrite(led, OFF);
digitalWrite(relay, LOW);
Serial.println("relay is off");
tri_seg_sw_state = NORMAL_OFF;
Blynk.virtualWrite(w_on_delay, 0);
Blynk.virtualWrite(w_seg_sw_st, tri_seg_sw_state);
Blynk.virtualWrite(w_button, digitalRead(relay));
break;
}
default:
Serial.println("unknow item select");
}
saveConfig();
}
//设置和保存on timer时间
BLYNK_WRITE(w_on_set)
{
on_time = param.asInt();
if (on_timer.isEnabled(on_id))
on_timer.restartTimer(on_id); //if timer was on, restart it
Blynk.virtualWrite(w_on_time, on_time); //or update remained time of widget
on_time_remain = on_time; // 通过更新on_time来改变timer定时时间
saveConfig();
msg = "on:" + String(on_timer.isEnabled(on_id)) + " off:" + String(off_timer.isEnabled(off_id));
Blynk.virtualWrite(w_msg, msg);
}
//设置和保存off timer时间
BLYNK_WRITE(w_off_set)
{
off_time = param.asInt();
if (off_timer.isEnabled(off_id))
off_timer.restartTimer(off_id);
Blynk.virtualWrite(w_off_time, off_time);
off_time_remain = off_time;
saveConfig();
msg = "on:" + String(on_timer.isEnabled(on_id)) + " off:" + String(off_timer.isEnabled(off_id));
Blynk.virtualWrite(w_msg, msg);
}
//load all config parameters when esp8266 poweron
bool loadConfig()
{
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile)
{
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024)
{
Serial.println("Config file size is too large");
return false;
}
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
StaticJsonBuffer<200> jsonBuffer;
JsonObject &json = jsonBuffer.parseObject(buf.get());
if (!json.success())
{
Serial.println("Failed to parse config file");
return false;
}
on_time = json["on_time"];
off_time = json["off_time"];
tri_seg_sw_state = json["seg_sw_state"];
if (on_time < MIN_ONTIME && on_time > MAX_ONTIME)
return false;
if (off_time < MIN_OFFTIME && off_time > MAX_OFFTIME)
return false;
if (tri_seg_sw_state < AUTO && tri_seg_sw_state > NORMAL_OFF)
return false;
// Real world application would store these values in some variables for
// later use.
Serial.print("Loaded on time: ");
Serial.println(on_time);
Serial.print("Loaded off time: ");
Serial.println(off_time);
return true;
}
//save all parameters when they changed
bool saveConfig()
{
StaticJsonBuffer<200> jsonBuffer;
JsonObject &json = jsonBuffer.createObject();
json["on_time"] = on_time;
json["off_time"] = off_time;
json["seg_sw_state"] = tri_seg_sw_state;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile)
{
Serial.println("Failed to open config file for writing");
return false;
}
else
{
Serial.println("param saved!");
}
json.printTo(configFile);
return true;
}
void setup()
{
// Debug console
Serial.begin(115200);
Serial.println(pwrOnStr[0]);
Serial.println(pwrOnStr[1]);
Serial.println(pwrOnStr[2]);
Serial.println(pwrOnStr[3]);
Serial.println(pwrOnStr[4]);
// init pin mode
pinMode(led, OUTPUT);
pinMode(standby_led, OUTPUT);
pinMode(relay, OUTPUT);
pinMode(button, INPUT_PULLUP); //enable internal pullup resistor
digitalWrite(relay, LOW);
ticker.attach(0.6, tick);
polling_button.attach_ms(1, buttonPolling); //1ms检查一次按键
wifi_config();
digitalWrite(led, OFF);
digitalWrite(standby_led, HIGH);
// connected to server:
Blynk.config(blynk_token, mqtt_server, port);
// load config parameters
if (!loadConfig())
{
on_time = 10;
off_time = 90;
tri_seg_sw_state = NORMAL_OFF; //load default value
}
// create timer id
on_time_remain = on_time;
off_time_remain = off_time;
doTriSwitch();
ds1820_init();
Blynk.syncAll();
pre_temp = DS18B20.getTempC(insideThermometer);
}
void loop()
{
unsigned long currentMillis = millis();
Blynk.run();
on_timer.run();
off_timer.run();
delay_timer.run();
if (OTA_FLAG)
{
httpServer.handleClient();
ArduinoOTA.handle();
}
switch (keyState)
{
case KEY_OTA:
{
ticker.attach(0.1, tick); //闪烁LED
ota_update();
//web ota config
MDNS.begin(WiFi.localIP().toString());
httpUpdater.setup(&httpServer);
httpServer.begin();
MDNS.addService("http", "tcp", 80);
Serial.printf("HTTPUpdateServer ready! Open http://");
Serial.print(WiFi.localIP().toString());
Serial.println("/update in your browser");
keyState = KEY_UP;
OTA_FLAG = true;
break;
}
case KEY_RESET:
{
Serial.println("clear wifi config");
wifiManager.resetSettings(); //erase stored parameters
keyState = KEY_UP;
Serial.println("reseting...");
delay(3000);
ESP.restart();
break;
}
case KEY_DOWN:
{
if (digitalRead(relay)) //如果是开启的则关闭
{
Serial.println("relay is off");
on_timer.deleteTimer(on_id);
delay_timer.deleteTimer(on_delay_id);
off_time_remain = off_time; //恢复定时值
off_id = off_timer.setTimer(ONE_MIN, one_min_off, off_time);
digitalWrite(relay, LOW);
digitalWrite(led, OFF);
Blynk.virtualWrite(w_on_delay, 0);
}
else
{
Serial.println("relay is on");
on_timer.deleteTimer(on_id);
off_timer.deleteTimer(off_id);
digitalWrite(relay, HIGH);
digitalWrite(led, ON);
//app上的led全部熄灭
w_on_led.off();
w_off_led.off();
//设置app上的定时时间为初始值
Blynk.virtualWrite(w_on_time, on_time);
Blynk.virtualWrite(w_off_time, off_time);
on_delay_time_remain = on_delay_time;
Blynk.virtualWrite(w_on_delay, on_delay_time);
delay_timer.setTimer(ONE_MIN, AutoturnOff, on_delay_time); //定时15分钟
}
keyState = KEY_UP;
Blynk.virtualWrite(w_button, digitalRead(relay)); //update w button status
break;
}
case KEY_UP:
{
break;
}
}
// check termperature if "interval" time has passed
if ((unsigned long)(currentMillis - previousMillis) >= temp_read_interval) //间隔temp_read_interval时间读取温度
{
Read_Temp();
// save the "current" time
previousMillis = millis();
}
}
// Run this function when connected to server
BLYNK_CONNECTED()
{
Serial.println("Connected!");
Blynk.virtualWrite(w_msg, msg);
Blynk.virtualWrite(w_title, String(pwrOnStr[1]) + " " + WiFi.localIP().toString());
Blynk.virtualWrite(w_button, digitalRead(relay));
Blynk.virtualWrite(w_seg_sw, tri_seg_sw_state);
Blynk.virtualWrite(w_on_time, on_time_remain);
Blynk.virtualWrite(w_off_time, off_time_remain);
Blynk.virtualWrite(w_on_set, on_time);
Blynk.virtualWrite(w_off_set, off_time);
Blynk.virtualWrite(w_on_delay, on_delay_time_remain);
}
//when app of smartphone connected,update widigets status
BLYNK_APP_CONNECTED()
{
Blynk.virtualWrite(w_msg, msg);
Blynk.virtualWrite(w_title, String(pwrOnStr[1]) + " " + WiFi.localIP().toString());
Blynk.virtualWrite(w_button, digitalRead(relay));
Blynk.virtualWrite(w_on_time, on_time_remain);
Blynk.virtualWrite(w_seg_sw, tri_seg_sw_state);
Blynk.virtualWrite(w_off_time, off_time_remain);
Blynk.virtualWrite(w_on_set, on_time);
Blynk.virtualWrite(w_off_set, off_time);
Blynk.virtualWrite(w_on_delay, on_delay_time_remain);
}