Blinker 开发实例介绍——温室大棚-Arduino中文社区 - Powered by Discuz! Archiver

armduino 发表于 2021-2-21 17:23

Blinker 开发实例介绍——温室大棚

申请免费成为认证开发者


硬件:ESP8266-D1-mini,光照传感器BH1750;温湿度传感器DHT11或22,土壤水份检测模块
平台:Arduino, Blinker 2.5.0


实现功能:
1)驱动继电器进而控制门禁、风机、阀门、灯光等设备
2)根据土壤墒情自动浇水
3)按钮及时反映控制状态

问题:
1)不能实时反馈时间数据,比如我要实现设备动作的倒计时显示在Button6.text("set_time")
2)MQTT经常掉线,WiFi经常会离线状态
3)ESP有时会重启
4)开关按钮有时会返回press而不是on
5)最近1周数据为啥只有4天?
6) 在callback函数中用了Blinker.delay()就会出现很多问题

#define BLINKER_WIFI

#include <Blinker.h>
#include <Wire.h>
#include <BH1750.h>
#include <DHT.h>
#include <Ticker.h>

#define DHTPIN D3//0~D3
#define DHTTYPE DHT11   // DHT 11
//#define DHTTYPE DHT22   // DHT 22(AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

BH1750 lightMeter;
DHT dht(DHTPIN, DHTTYPE);

char auth[] = "ae800518f46e";//Your Device Secret Key";
char ssid[] = "TP-LINK_2F98";//Your WiFi network SSID or name";
char pswd[] = "xxxxxxxx";//Your WiFi network WPA password or WEP key";

bool switch_state = false; //开关状态
bool b6_state= false;
int jscs=0; //浇水次数
int jsq=0;//计时器
int set_time=0; //用滑块设定浇水时间
int last_time=0;
uint32_t read_time = 0;
uint8_t wet=0;   //土壤湿度,显示在APP中
uint8_t wet2=0;//土壤湿度,用于控制
uint8_t soil_read=0;
uint16_t light_read=0;
float humi_read=0, temp_read=0;
int8_t ihour=0 ; //小时
int8_t imin=0 ;//分钟
int8_t isec=0 ;//秒
char buf1[]="开";
char buf2;
char buf3="s";
char dsk;   //定时开几秒
Ticker flipper;             //创建定时器对象
BlinkerNumber HUMI("humi"); //创建数据按钮对象,并赋初值humi
BlinkerNumber TEMP("temp");
BlinkerNumber LIGHT("light");
BlinkerNumber SOIL("soil");
BlinkerNumber Number1("num-1");

BlinkerButton Button1("btn-door");//实例化开关按钮对象,值btn-door与手机APP对应按钮名称一致
BlinkerButton Button2("btn-lamp");//照明
//BlinkerButton Button3("btn-ac");//备用
BlinkerButton Button4("btn-fan");   //风机
BlinkerButton Button5("btn-auto");//自动浇
BlinkerButton Button6("btn-valv1"); //手动浇,定时

BlinkerSlider Slider1("set-time");//设置滑块对象

void slider1_callback(int32_t value){

   set_time=value;//自定义,单位毫秒

   BLINKER_LOG("get slider value:",value*1000," s");
}

void switch_callback(const String & state){//设备总开关
   digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));//总是让LED灯发生明灭变化
   BLINKER_LOG("get builtin switch state: ", state);

    if (state == BLINKER_CMD_ON) {
      switch_state = true;
      digitalWrite(D7, HIGH);//实验用D7引脚,实际可能会用别的
      BUILTIN_SWITCH.print("on");
    }
    else if (state == BLINKER_CMD_OFF) {
      switch_state = false;
      digitalWrite(D7, LOW);
      BUILTIN_SWITCH.print("off");
    }
}

//门禁控制
void button1_callback(const String & state){
   BLINKER_LOG("get door switch state: ", state);
   if (state == "on") {
      digitalWrite(D5,HIGH);
      Button1.icon("fas fa-door-open");
      Button1.text("打开啦");
      Button1.print("on");// 返回状态信息,在手机调试窗口会显示
      }
   else if (state=="off"){
      digitalWrite(D5, LOW);
      Button1.icon("fas fa-door-closed");
      Button1.text("门已闭");
      Button1.print("off");
      }
}

//灯光控制
void button2_callback(const String & state){
   BLINKER_LOG("get lamp switch state: ", state);
   if (state == "on") {
      digitalWrite(D6,HIGH);
      Button2.text("照亮梦想");
      Button2.print("on");
      }
   else {
      digitalWrite(D6, LOW);
      Button2.text("黑黢黢");
      Button2.icon("fas fa-lightbulb");
      Button2.print("off");
      }
}

//风机控制
void button4_callback(const String & state){
   BLINKER_LOG("get fan switch state: ", state);
   if (state == "on") {
      digitalWrite(D6,HIGH);
      Button4.icon("fa fa-fan fa-spin");//使用 fa-spin类,使任意图标旋转,还可以使用fa-pulse使其进行8方位旋转
      Button4.text("转晕了");            //尤其适合 fa-spinner,fa-fan,fa-refresh,fa-cog
      Button4.print("on");
      }
   else {
      digitalWrite(D6, LOW);
      Button4.text("休息中");
      Button4.print("off");
      }
}

//自动运行模式按钮
void button5_callback(const String & state){
   BLINKER_LOG("get btn5 state: ", state);
   if (state == "on") {
      Button5.icon("fa fa-cog fa-spin");
      autodrip();                         //调用水份监测控制函数
      Button5.text("自动运行");
      Button5.print("on");
      }
   else {
      digitalWrite(D7,LOW);
      Button5.text("停用自动");
      Button5.print("off");
      }
}

//手动控制,并设定了时间变量   
void button6_callback( const String & state){
   BLINKER_LOG("get switch state: ", state);
   if (state == "on" ){
      digitalWrite(D8, HIGH);

      sprintf(buf2,"%d",set_time);         //滑块返回设定时间
      sprintf(dsk,"%s%s%s",buf1,buf2,buf3);//buf1=开,buf3=s
      Button6.text(dsk);
      Button6.icon("far fa-faucet-drip");
      Button6.print("on");                     //第一次调用显示龙头状态

      Blinker.delay(set_time*1000);
      digitalWrite(D8, LOW);               //延时set_time*1000秒后自动停止D8控制的对象

      sprintf(buf2,"%d",0);         
      sprintf(dsk,"%s%s%s",buf1,buf2,buf3);
      Button6.text(dsk);
      Button6.icon("fad fa-faucet-drip");
      Button6.print("on");                  //第二次调状态
      b6_state= true;
   }
      else if (state == "off"){
      digitalWrite(D8, LOW);
      Button6.icon("fas fa-faucet");
      Button6.text("阀门关");
      Button6.print("off");
      b6_state= false;
      }      
}

// 如果未绑定的组件(没有定义的按钮,输入框)被触发,则会执行dataRead()函数
void dataRead(const String & data){
   BLINKER_LOG("Blinker readString: ", data);//在串口显示为 Blingker readString:
   Blinker.vibrate(255);//发送手机振动指令, 震动时间, 单位ms, 数值范围0-1000, 默认为500
   uint32_t BlinkerTime = millis();
   Blinker.print("millis", BlinkerTime);//在手机monitor显示
   digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));//当有按钮动作发生时,总是让LED发生明灭的变化
   /*
   if (data=="123"){//如果在输入框输入“123”,本例没用到,因为屏幕被别的元素占满了
      //autodrip();
       digitalWrite(D7,HIGH);
       Blinker.delay(1500);
       digitalWrite(D7,LOW);
   }
   else
       digitalWrite(D7,LOW);
   */
   BLINKER_LOG("input=",data);
}

//心跳包使得我们每次打开APP都可以看到实时的最新数据
//WiFi设备每59秒会返回一次心跳包;Ble设备每29秒返回一次心跳包
void heartbeat(){         
   if (switch_state) BUILTIN_SWITCH.print("on");
   else BUILTIN_SWITCH.print("off");

    //Number1.print(WiFi.RSSI()); //把信号强度发送给APP端
   Number1.print(jsq);      //获取开机运行到现在的时间
   Number1.icon("fas fa-clock fa-spin");//定义一个旋转的时钟

   TEMP.print(temp_read);    //免费用户最多分配5个数据显示BlinkerNumber对象.print()
   HUMI.print(humi_read);    //反馈湿度数据给APP
   SOIL.print(soil_read);
   LIGHT.print(light_read);

   //HUMI.icon("fas fa-tint"); //设置ui组件图标,在图标库 (https://fontawesome.com) 中找到要使用的图标
   //HUMI.color("#0099FF");    //也可以在APP中进行配置   
   TEMP.icon("fas fa-thermometer");
   //TEMP.color("#FF3300");
   LIGHT.icon("fas fa-cloud-sun");
}

//储存数据在ui中以图表显示,最多显示3个图表
void dataStorage() {
   Blinker.dataStorage("temp", temp_read);
   Blinker.dataStorage("humi", humi_read);
   Blinker.dataStorage("light",light_read);
    //Blinker.dataStorage("soil",soil_read);
}
/*
void flip(){
   int senVal=constrain(analogRead(A0),50,500);
   wet = map(senVal,50,500,0,100); //获取湿度
   BLINKER_LOG("senVal=",senVal,", wet=",wet);
}
*/
void autodrip(){
   ihour = Blinker.hour();   //获取当前时间小时数, 单位为小时(h), 获取成功时值: 0-23, 获取失败时值: -1
   if (ihour==23 && imin == 15 && isec <= 30){//每到晚上23:15分,模块自动重启.
   ESP.restart();                               //重启后jscs会重置为零,实现每天最多浇水两次
   }
   BLINKER_LOG("今日第 <",ihour,"> 小时");
   wet2=analogRead(A0);
   if ( wet2 <= 80 && jscs <= 2 ) {   //水份含量设定,jscs自动浇水次数
   jscs++;
   digitalWrite(D7,HIGH);
   Button5.text("自动浇灌");
   Button5.print("on");

   BLINKER_LOG("水分含量", wet2, "%,开始自动浇水");
   BLINKER_LOG("这是第 <",jscs,"> 次浇水");
   Blinker.delay(10000);//每次自动浇水10秒或者别的时间

   digitalWrite(D7,LOW);

   Button5.text("自动停止");
   Button5.print("off");
   }   
   BLINKER_LOG("今日总浇水次数为:",jscs);
}

void setup(){

   Wire.begin(D2,D1);   
   Serial.begin(115200);
   BLINKER_DEBUG.stream(Serial);
    //BLINKER_DEBUG.debugAll();
   pinMode(LED_BUILTIN, OUTPUT);
   pinMode(D5,OUTPUT);//door
   pinMode(D6,OUTPUT);//lamp
   pinMode(D7,OUTPUT);//manu
   pinMode(D8,OUTPUT);//auto
   digitalWrite(LED_BUILTIN, LOW);

   Blinker.begin(auth, ssid, pswd);
   Blinker.attachData(dataRead);
   Blinker.attachHeartbeat(heartbeat);

   Blinker.attachDataStorage(dataStorage);
   BUILTIN_SWITCH.attach(switch_callback);//设备总开关
   Slider1.attach(slider1_callback);
   Button1.attach(button1_callback);//当app中组件触发并发送到设备端时将触发该组件注册的回调函数
   Button2.attach(button2_callback);
   Button4.attach(button4_callback);
   Button5.attach(button5_callback);
   Button6.attach(button6_callback);
   //flipper.attach(5, flip);                //定时器每5秒调用一次flip函数   
   dht.begin();
   lightMeter.begin();//(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire);

   BLINKER_LOG("BH1750 light sensor test begin...");
   if (lightMeter.begin()) {
      BLINKER_LOG("BH1750 light sensor initialised 光照传感器初始化完成!");
   }
   else {
      BLINKER_LOG("Error initialising BH1750");
   }

   Blinker.setTimezone(8.0);
   BLINKER_LOG("Now ntp time: ", Blinker.time());
    //ihour = Blinker.hour();
    //imin = Blinker.minute();
    //isec = Blinker.second();
    //BLINKER_LOG("当前时间:",ihour,"-",imin,"-",isec);
}

void loop(){
   Blinker.run();//此函数需要频繁调用以保持设备间连接及处理收到的数据
    if (last_time==0 || (millis()-last_time) >= 60000){
      //imin=Blinker.minute();
      last_time=millis();
      jsq++;
    }
   //开机取一次,以后 3s 间隔取一次数据.但是间隔 59s APP更新一次
   uint16_t data_time;
         data_time= 3000;
   if (read_time == 0 || (millis() - read_time) >= data_time) {
      read_time = millis();
      int wet = analogRead(A0);    //获取湿度值
      uint16_t lux = lightMeter.readLightLevel();
      float h = dht.readHumidity();
      float t = dht.readTemperature();      

      if (isnan(h) || isnan(t)) {
          BLINKER_LOG("Failed to read from DHT sensor!");
          return;
      }

       float hic = dht.computeHeatIndex(t, h, false);

       humi_read = h;
       temp_read = t;
       light_read = lux;
       soil_read = wet;
       BLINKER_LOG("Temp: ", t, " *C\tHumi: ", h, " %\tLight: ",lux," lux\tWet:",wet," %");
       //BLINKER_LOG("Temperature: ", t, " *C");
       BLINKER_LOG("Heat index体感温度: ", hic, " *C");

    }
    //Blinker.delay(2000);
}


armduino 发表于 2021-2-21 17:25

奈何col 发表于 2021-2-21 20:31

1)不能实时反馈时间数据,比如我要实现设备动作的倒计时显示在Button6.text("set_time")
目前设计就是这样的,倒计时定时可以通过定时页面显示
2)MQTT经常掉线,WiFi经常会离线状态
8266 ram不够,在上传数据到云端时会断开MQTT,再切换到http进行,上传完成后,即会从新连接mqtt
3)ESP有时会重启
看报错信息,程序问题,ram不足,供电问题都可能导致设备重启
4)开关按钮有时会返回press而不是on
长按就会触发press
5)最近1周数据为啥只有4天?
不清楚,建议过几天再看,如果还是只有近4天的,再联系我
6) 在callback函数中用了Blinker.delay()就会出现很多问题
回调函数中,不能有耗时的操作,任何单片机上都是如此

armduino 发表于 2021-2-22 12:26

armduino 发表于 2021-2-22 12:28

谢谢回复。回调函数的问题我正在解决中。
图表里面只能显示4个时间点,一直是这样。

armduino 发表于 2021-2-22 14:54

修改后:
bool b5_state=false;
bool b6_state=false;
void button6_callback( const String & state){
   BLINKER_LOG("get switch state: ", state);
   if (state == "on" ){
      b6_state=true;
   }
      else if (state == "off"){
      b6_state=false;
      digitalWrite(D8, LOW);
      Button6.icon("fas fa-faucet");
      Button6.text("阀门关");
      Button6.print("off");
      }      
}
void loop(){
   Blinker.run();
   
   if (b5_state==true){
      autodrip();
      b5_state=false;
   }
   
   if (b6_state==true){
      manudrip();
      b6_state=false;
   }
......
}

void manudrip(){
      digitalWrite(D8, HIGH);
         
   for (int i=set_time; i>=0; i--){      //增加for循环,b6按钮的时间实现倒计时显示
      sprintf(buf2,"%d",i);               //滑块返回设定时间
      sprintf(dsk,"%s%s%s",buf1,buf2,buf3); //buf1=开,buf3=s
      Button6.text(dsk);
      Button6.icon("far fa-faucet-drip");
      Button6.print("on");                  //第一次调用显示龙头状态   
      Blinker.delay(1000);
      if (b6_state==false)
      break;
      }
      //Blinker.delay(set_time*1000);
      digitalWrite(D8, LOW);               //延时set_time*1000秒后自动停止D8控制的对象
      
      sprintf(buf2,"%d",0);         
      sprintf(dsk,"%s%s%s",buf1,buf2,buf3);
      Button6.text(dsk);
      Button6.icon("fad fa-faucet-drip");
                        
      Blinker.delay(1000);
      Button6.text("再按复位");
      Button6.print("on");//第二次调状态
}

倒计时一旦开始(滑块set_time=10s),总是出现MQTT 不稳问题。

Connecting to MQTT...
reconnect_time: 0
MQTT Connected!
Freeheap: 6720
Temp: 22.20 *C        Humi: 42.00 %        Light: 39 lux        Wet:536 %
Heat index体感温度: 21.57 *C
Temp: 22.20 *C        Humi: 44.00 %        Light: 36 lux        Wet:532 %
Heat index体感温度: 21.62 *C
get switch state: on
ERROR: MQTT NOT ALIVE OR MSG LIMIT
ERROR: MQTT NOT ALIVE OR MSG LIMIT
ERROR: MQTT NOT ALIVE OR MSG LIMIT
ERROR: MQTT NOT ALIVE OR MSG LIMIT
ERROR: MQTT NOT ALIVE OR MSG LIMIT
ERROR: MQTT NOT ALIVE OR MSG LIMIT
Temp: 22.20 *C        Humi: 43.00 %        Light: 37 lux        Wet:489 %
Heat index体感温度: 21.60 *C
Temp: 22.10 *C        Humi: 41.00 %        Light: 37 lux        Wet:488 %
Heat index体感温度: 21.44 *C
页: [1]
查看完整版本: Blinker 开发实例介绍——温室大棚