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

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 1860|回复: 5

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

[复制链接]
发表于 2021-2-21 17:23 | 显示全部楼层 |阅读模式
申请免费成为认证开发者


硬件: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[10];
char buf3[2]="s";
char dsk[10];   //定时开几秒
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);
}


 楼主| 发表于 2021-2-21 17:25 | 显示全部楼层
Screenshot_20210221_153613_iot.clz.me.jpg
发表于 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()就会出现很多问题
回调函数中,不能有耗时的操作,任何单片机上都是如此
 楼主| 发表于 2021-2-22 12:26 | 显示全部楼层
Screenshot_20210222_122321_iot.clz.me.jpg
 楼主| 发表于 2021-2-22 12:28 | 显示全部楼层
谢谢回复。回调函数的问题我正在解决中。
图表里面只能显示4个时间点,一直是这样。
 楼主| 发表于 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 不稳问题。

[1096875] Connecting to MQTT...
[1096875] reconnect_time: 0
[1097628] MQTT Connected!
[1097628] Freeheap: 6720
[1098127] Temp: 22.20 *C        Humi: 42.00 %        Light: 39 lux        Wet:536 %
[1098128] Heat index体感温度: 21.57 *C
[1101142] Temp: 22.20 *C        Humi: 44.00 %        Light: 36 lux        Wet:532 %
[1101142] Heat index体感温度: 21.62 *C
[1101544] get switch state: on
[1102660] ERROR: MQTT NOT ALIVE OR MSG LIMIT
[1104670] ERROR: MQTT NOT ALIVE OR MSG LIMIT
[1106688] ERROR: MQTT NOT ALIVE OR MSG LIMIT
[1108716] ERROR: MQTT NOT ALIVE OR MSG LIMIT
[1110733] ERROR: MQTT NOT ALIVE OR MSG LIMIT
[1112755] ERROR: MQTT NOT ALIVE OR MSG LIMIT
[1114706] Temp: 22.20 *C        Humi: 43.00 %        Light: 37 lux        Wet:489 %
[1114706] Heat index体感温度: 21.60 *C
[1117723] Temp: 22.10 *C        Humi: 41.00 %        Light: 37 lux        Wet:488 %
[1117724] Heat index体感温度: 21.44 *C
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 14:51 , Processed in 0.395044 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表