BLYNK
HOME       📲 GETTING STARTED       📗 DOCS       ❓HELP CENTER       👉 SKETCH BUILDER

Why "superchat" widget display value is very difference with "labeled value" widget

When I use app to display temperature reading from ESP8266, and I show live temperature with “labeled value”(V11),and show history temperatures curve with “superchart”(get data directly from V11). I found that Superchart display value was very different with “labeled value”. In same time, the temperature difference they show is about 5 degrees. Temperature value in “superchart” always lower han “labeled value”. I don’t know why? Pls help !

• ESP8266
• android 8.1
• local server
• Blynk Library version 0.6.1

My partial code as below:

//read ds1820
float pre_temp = 0;
void Read_Temp()
{
  float now_temp = DS18B20.getTempC(insideThermometer);
  Serial.print("Temp C: ");
  Serial.println(now_temp);
  Blynk.virtualWrite(V11, now_temp);
  
  if ((now_temp - pre_temp) > 0.2 && digitalRead(relay)) 
  {
    digitalWrite(standby_led, LOW); //turn off led
  }
  else if (tri_seg_sw_state == NORMAL_OFF) 
  {
    digitalWrite(standby_led, LOW);
  }
  else
  {
    digitalWrite(standby_led, HIGH); //turn on led
  }
  pre_temp = now_temp;
}

unsigned long previousMillis = 0;
unsigned long temp_read_interval = 5000; //every 5 seconds to read ds1820
void loop()
{
  unsigned long currentMillis = millis();
  DS18B20.requestTemperatures(); 
  Blynk.run();
  // check termperature if "interval" time has passed
  if ((unsigned long)(currentMillis - previousMillis) >= temp_read_interval) 
  {
    Read_Temp();
    // save the "current" time
    previousMillis = millis();
  }
}

Maybe due to averaging?
See the Granularity comments at the end of the SuperChart documentation:

Pete.

I just see this in superchart document:
2. Tags SuperChart can aggregate data from multiple devices using built-in aggregation functions. For example, if you have 10 Temperature sensors sending temperature with the given period, you can plot average value from 10 sensors on the widget.

In my app, I only use one temperature device and don’t use “tag” mode.

Leon

That isn’t the part of the document I was referring to.

Pete.

I don’t know which part you referred to?
My app’s picture as following, in last point in superchart shows temperature is 61.9,but this time is’s real temperature is 67.0 on left bottom.
1

Exactly what I said…

Superchart supports currently 2 types of granularity:

  • Minute granularity - 1h , 6h , 1d ;
  • Hour granularity - 1w , 1m , 3m ;

This means that minimum chart update interval is 1 minute for 1h , 6h , 1d periods. 1 hour for 1w , 1m and 3m periods. As Blynk Cloud is free to use we have a limit on how many data you can store. At the moment Blynk Cloud accepts 1 message per minute per pin. In case you send your data more frequently your values will be averaged. For example, in case you send value 10 at 12:12:05 and than again 12 at 12:12:45 as result in chart you’ll see value 11 for 12:12.

In order to see data in chart you need to use either widgets with “Frequency reading” interval (in that case your app should be open and running) or you can use Blynk.virtualWrite on hardware side. Every Blynk.virtualWrite command is stored on server automatically. In that case you don’t need application to be up and running.

Pete.

???

Pete.

Sorry, sent to wrong place. :slight_smile:

Hi,Pete
I found another issue for “labeled value” widget. See picture
1
V11 is yellow box. At the same time, I use terminal widget to print now_temp as red box in picture
I found V11 offten get “1.0” value,but there is no “1.0” value in terminal ever.
What am I wrong? Or “labeled value” widget has some bug?

In the same project as above. My code are below:

float pre_temp = 0;
void Read_Temp()
{
  float now_temp = DS18B20.getTempC(insideThermometer);
  Blynk.virtualWrite(V11, now_temp);
  terminal.print("t=");
  terminal.println(now_temp);
  terminal.flush();
  if ((now_temp - pre_temp) > 0.2 && digitalRead(relay)) 
  {
    digitalWrite(standby_led, LOW); //turn off led
  }
  else if (tri_seg_sw_state == NORMAL_OFF) 
  {
    digitalWrite(standby_led, LOW);
  }
  else
  {
    digitalWrite(standby_led, HIGH); //turn on led
  }
  pre_temp = now_temp;
}

unsigned long previousMillis = 0;
unsigned long temp_read_interval = 5000; //read ds1820 interval
void loop()
{
  unsigned long currentMillis = millis();
  DS18B20.requestTemperatures(); 
  Blynk.run();
  // check termperature if "interval" time has passed
  if ((unsigned long)(currentMillis - previousMillis) >= temp_read_interval) 
  {
    Read_Temp();
    // save the "current" time
    previousMillis = millis();
  }
}

I’m betting that you’ve hit the data mapping squiggly line in the labelled value widget setup…

Data Mapping

In case you want to map incoming values to specific range you may use mapping button:

Let’s say your sensor sends values from 0 to 1023. But you want to display values in a range 0 to 100 in the app. When Data Mapping enabled, incoming value 1023 will be mapped to 100.

Pete.

My labeled value setting as below.


Whatever I set range as 5-100,or 0-100,or 0-1023, it always will receive 1.0 value. When I light up Data mapping as you said2 , then its value are not equal to real value.
I think this is why my “superchart” value is more lower than real value. Because it’s on average with 1.0 :joy:

I guess the Labeled Value Widget doesn’t like the float value.

Try this

Blynk.virtualWrite(V11, String(now_temp, 1) );

or

Blynk.virtualWrite(V11, String(now_temp, 2) );

Hi,knoih
In the Blynk document, virtualwrite says value can be string,integer,float,etc.As picture
1

You have big problem in the loop() of your code, that might explain the erratic behaviour of Blynk, for example: corrupted data, etc. The Onewire could also be bombarded by requests.

void loop()
{
  unsigned long currentMillis = millis();
  DS18B20.requestTemperatures();    <============= can't be here, at this speed
  Blynk.run();
  // check termperature if "interval" time has passed
  if ((unsigned long)(currentMillis - previousMillis) >= temp_read_interval) 
  {
    Read_Temp();
    // save the "current" time
    previousMillis = millis();
  }
}

Remember popular clean loop() message of @PeteKnight.

I suggest you change the code to something like this:

#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h>

//You have to download Blynk WiFiManager Blynk_WM library at //https://github.com/khoih-prog/Blynk_WM
// In order to enable (USE_BLYNK_WM = true). Otherwise, use (USE_BLYNK_WM = false)
#define USE_BLYNK_WM   true
//#define USE_BLYNK_WM   false

#define USE_SSL     false

#if USE_BLYNK_WM
  #if USE_SSL
    #include <BlynkSimpleEsp8266_SSL_WM.h>        //https://github.com/khoih-prog/Blynk_WM
  #else
    #include <BlynkSimpleEsp8266_WM.h>            //https://github.com/khoih-prog/Blynk_WM
  #endif
#else
  #if USE_SSL
    #include <BlynkSimpleEsp8266_SSL.h>
    #define BLYNK_HARDWARE_PORT     9443
  #else
    #include <BlynkSimpleEsp8266.h>
    #define BLYNK_HARDWARE_PORT     8080   
  #endif
#endif

#if !USE_BLYNK_WM
  #define USE_LOCAL_SERVER    true
  
  // If local server
  #if USE_LOCAL_SERVER
    char blynk_server[]   = "yourname.duckdns.org";
    //char blynk_server[]   = "192.168.2.110";
  #else
    char blynk_server[]   = "";
  #endif

char auth[]     = "***";
char ssid[]     = "***";
char pass[]     = "***";

#endif

#include <DallasTemperature.h>

#define PIN_TH        4             //D2 + LED on nodemcu, D4/SDA on Arduino WeMos ESP8266

OneWire* oneWire;
DallasTemperature* DS18B20;

#define DHT_DEBUG     true

//float pre_temp = 0;
void Read_Temp()
{
  float now_temp;
  
  DS18B20->requestTemperatures();  
      
  now_temp = DS18B20->getTempCByIndex(0);
  //now_temp = DS18B20->getTempC(0);
    
  #if DHT_DEBUG
    if (isnan(now_temp))
      Serial.println("read_TH_sensor: DS18B20 temp is NAN");
    else
      Serial.printf("read_TH_sensor: DS18B20 temp = %5.2f\n", now_temp);
  #endif           

  Blynk.virtualWrite(V10, now_temp);

  if (!isnan(now_temp))
  {
    Blynk.virtualWrite(V11, now_temp);
    //Blynk.virtualWrite(V11, String(now_temp, 2));
  }
  

  #if 0
    float now_temp = DS18B20.getTempC(insideThermometer);
    Blynk.virtualWrite(V11, now_temp);
    terminal.print("t=");
    terminal.println(now_temp);
    terminal.flush();
    if ((now_temp - pre_temp) > 0.2 && digitalRead(relay)) 
    {
      digitalWrite(standby_led, LOW); //turn off led
    }
    else if (tri_seg_sw_state == NORMAL_OFF) 
    {
      digitalWrite(standby_led, LOW);
    }
    else
    {
      digitalWrite(standby_led, HIGH); //turn on led
    }
    pre_temp = now_temp;
  #endif
  
}

void setup()
{
  Serial.begin(115200);
 
  Serial.println("\nStarting");
  
  oneWire = new OneWire(PIN_TH);
  DS18B20 = new DallasTemperature(oneWire);
  DS18B20->begin();

  #if USE_BLYNK_WM
    Blynk.begin();
  #else
    WiFi.begin(ssid, pass);
    
    #if USE_LOCAL_SERVER
      Blynk.config(auth, blynk_server, BLYNK_HARDWARE_PORT);
    #else
      Blynk.config(auth);
    #endif
    
    Blynk.connect();
    if ( Blynk.connected())
      Serial.println("Connected to Blynk");   
  #endif
   
}

unsigned long previousMillis = 0;

#define TEMP_READ_INTERVAL        5000          //read ds1820 interval

void loop()
{
  static unsigned long currentMillis;
  
  currentMillis = millis();
  
  // Can not leave it here in the loop, running very very high frequency => possible erratic Blynk behaviour
  //DS18B20->requestTemperatures(); 
  
  Blynk.run();
  // check termperature if "interval" time has passed
  if ( (currentMillis - previousMillis) >= TEMP_READ_INTERVAL )
  {
    Read_Temp();
    // save the "current" time
    previousMillis = millis();
  }
}

It’s tested OK and has no data corruption issue.

PS: Next time, it’s advisable to post full code if you’d like the help of other people. Nothing to hide here at all. Otherwise. it’ll take more time, as the problem might be lurking somewhere in your hidden code.
It also take more other people’s time to research and duplicate the problem.

I confirm that sending float data to Labeled Value Widget is OK. My principle is never trust 100% in any document, but have to experiment if problem arises.

Hi,knoih
I think you didn’t read my previous posts,pls see this picture and code
1
First,In my app, terminal log said that all data from ds1820 are right(red box), only "labeled value"widget is wrong(yellow box).
Second, in my code(as bellow),at same time ,I put same data(now_temp) to V11 and terminal. But terminal is right and widget is wrong.
So, my code has no problem I think.

//read ds1820
float pre_temp = 0;
void Read_Temp()
{
  float now_temp = DS18B20.getTempC(insideThermometer);
  Blynk.virtualWrite(V11, now_temp);
  terminal.print("t=");
  terminal.println(now_temp);
  terminal.flush();
.....
}

Have you tried deleting the V11 widget and adding it back in?
When you do this, ensure that it doesn’t say that pin V11 is busy.

Pete.

Knoih,
I am so sorry that I made a fool mistake,I defined two V11 in two place that I didn’t find.:scream:
DS1820 is the new device adding to this project a few days ago, I forgot V11 had been used.

#define w_seg_sw_st V11 //status switch
......
#define w_temp V11      //water temperature

w_seg_sw has three value->1,2,3. It’s normally stay to status 1.

Thanks Pete and Knoih!

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

Wow. You’re so good at coding. Much better than what you show in previous post.

It’ll take long time to go through your code if necessary.

But just some small advice is that you break the code into several independent files, so that it’s easier to manage. Such as Blynk-related, Main, OTA, etc.

I also spot some typo mistake, but that is nothing as the compiler will sort it out in no time.

Also you’re creating too many instances of BlynkTimers, while a BlynkTimer can handle up to 16 independent timers. But only you can know and decide if it’s absolutely necessary now.

Just a question is that which library your <Timer.h> is using.

Hi, Khoih
That’s a good ideal for split code to several files. I saw example code do use one file to do all things, so I think it can use only one file for one project:joy:
At beginning of this project, I used only one BlynkTimer to do all things, but soon I found they would interfere with each other, so I had to use three timer.
About <timer.h>,it seems no use, maybe used before using Blynk, I will comment it later.
BTW, there is a problem that has plagued me for a long time. My code has include more header files, and long time passed, I didn’t remember these header files belong which library. If I uninstall and reinstall build environment, how can I know which library should I install?Because arduino has more library with similar feature. Do you know what I mean?