從零開始的DIY智能家居 - 基於 ESP32 的智能澆水器


前言

上次 土壤濕度傳感器 完成之后,就立下一個 flag 要搭建一個智慧澆水的智能場景,現在終於有時間填坑了!(o゚▽゚)o

智慧澆水場景的核心設備有三個:
檢測土壤狀態的:土壤濕度傳感器 通過這個傳感器來獲取土壤信息,作為是否澆水的依據。
智能澆水器:執行裝置,通過 Spirit 1 控制。
Spirit 1

這次就來制作智慧澆水的智能場景的核心: 智能澆水器,我准備買一個便宜的傻不拉幾的澆水器自己改造一下,想辦法給他連上腦子。
在這里插入圖片描述
主要交互流程如下圖:
在這里插入圖片描述

(σ゚∀゚)σ..:*☆哎喲不錯哦,是不是很厲害啊!


硬件選擇

萬年不變的 安信可的 ESP32S ,別問,問就是便宜才 24元。

繼電器,因為不清楚澆水器電路情況保險起見,使用了繼電器進行隔離,4.5元

澆水器 淘寶隨便找的 99元,選擇它是因為這個方便改造,有一個可以拆卸的電池盒方便塞開發板和繼電器,按鈕是機械式的,可以通過繼電器短接模擬按鈕效果進行控制,並且有一個手動澆水的功能,也就是按鈕摁一下就澆水,再摁一下就關閉,我們從這個功能下手。
在這里插入圖片描述
(寫文章的時候這東西已經被我拆掉了,就拿淘寶的圖湊活一下吧,圖上按的中間按鈕就是我們需要接管的按鈕)

(((((((((((っ•ω•)っ Σ(σ`•ω•´)σ 起飛!

改造接線

硬件都到了之后就開始改造電路!
控制電路:
控制電路
澆水器面板中間的按鈕就是手動控制按鈕下降沿觸發,而我們在這里使用了一個繼電器常開端接到按鈕上,當開發板 12號 IO 口給繼電器電壓時,繼電器常開端閉合,按鈕被短接,兩端電壓被拉至5V,0.1S后斷開,電壓拉低,下降沿觸發。

休眠檢測電路:
在這里插入圖片描述

澆水器中有一個10S左右沒有控制就進入休眠狀態的設置我們沒辦法修改,進入休眠狀態后需要一個額外的觸發來喚醒澆水器,而澆水器喚醒時,會點亮數碼管,於是就通過 A0 引腳接到數碼管的共陽級,如果檢測到數碼管的共陽級為低電平,就認為澆水器進入休眠狀態,在觸發命令之前額外觸發一次,解除澆水器的休眠狀態。

澆水器工作狀態檢測電路:
電機檢測

澆水器面板通過信號線來控制下面水泵電機的工作,這里我通過5號 IO 監控信號線的電壓來確定電機的工作狀態。

代碼解析

為了方便講解邏輯,我會打亂代碼的順序可能還會進行裁剪,要是想直接拿代碼跑的朋友可以直接去 靈感桌面的秘密寶庫 獲取代碼,或者直接 clone:

https://gitee.com/inspiration-desktop/DEV-lib-arduino.git

要是連 git 是什么都不知道,可以參考簡單無腦,上手即用 - 手把手教你使用 智能紅外溫度傳感器代碼以及依賴的 gitee 庫!
下載或者 clone 代碼后這次用到的是這個三個文件夾:

在這里插入圖片描述
cjson:我移植的 cjson 庫,就是標准的 cjson 庫,放到 arduino 安裝目錄下的 libraries 文件夾里,百度一下 cjson 的函數使用就行了。

libsddc:是我移植自官方的SDDC庫和自己寫的 SDK,也是放入 libraries 文件夾里就行。里面是 SDDC 協議的處理函數,我們不用管。

demo 文件夾里面就是我們各種傳感器的 demo 代碼了:
在這里插入圖片描述
具體 arduino 使用教程可以看我之前的文章 arduino開發指導手把手帶你 arduino 開發:基於ESP32S 的第一個應用-紅外測溫槍(帶引腳圖)

設備控制命令:

通過 Spirit 1 的應用程序或者 嗅探器 向傳感器設備發送的命令:
通過向澆水器發送 "ON"/"OFF" 的 set 命令可以控制澆水器是否澆水:

{
    "method": "set",                      // 控制澆水器開始/停止澆水
    "watering": "ON"/"OFF"
}

通過向澆水器發送 "watering" 的 get 命令可以獲取澆水器是否有在澆水:

{
    "method": "get",                     // 獲取澆水器工作狀態
    "obj": ["watering"]
}

設備和協議初始化流程:

基於官方 demo 寫的不需要做什么修改,主要是設備初始化,管腳配置,和協議初始化部分。

因為涉及到 IO 口的輸入和輸出,所以需要手動配置一下 IO 口狀態。並且創建一個一個消息隊列來儲存和傳遞收到的命令

void sensor_init()
{
    pinMode(water_pin, OUTPUT);
    pinMode(sign_pin, INPUT);
    pinMode(monitor_pin,INPUT);

    // 設置一個消息隊列來緩存命令,防止命令丟失
    Message_Queue = xQueueCreate(MESSAGE_Q_NUM, MESSAGE_REC_LEN);                 //創建消息Message_Queue      
    if(Message_Queue == 0)
    {
          printf("隊列 Message_Queue 創建失敗!\r\n");                        
    }
}

void setup() {
    // 這部分主要是協議初始化和設備初始化,沒有需要修改的地方,詳見gitee庫
}

void loop() {
    // 這部分主要是協議初始化和設備初始化,沒有需要修改的地方,詳見gitee庫
}

配置設備信息

這部分代碼可以配置 WiFi 名字和 WiFi 密碼,要使用的引腳,並且配置設備在 Spirit 1 上顯示的信息:

// 依賴度頭文件和庫
#include "Arduino.h"    
#include <OneButton.h>       
#include <WiFi.h>
#include <sddc.h>
#include <cJSON.h>
#include <Wire.h>
#include <SDDC_SDK_lib.h>

#define SDDC_CFG_PORT         680U                 // SDDC 協議使用的端口號
#define PIN_INPUT 0                                // 選擇 IO0 進行控制
#define ESP_TASK_STACK_SIZE   4096
#define ESP_TASK_PRIO         25 
#define MESSAGE_Q_NUM         5                    // 數據的消息隊列的數量 
#define MESSAGE_REC_LEN       5                    // 數據的消息隊列的長度

static sddc_t *g_sddc;
static const char* ssid = "TP-LINK_54F9C2";        // WiFi 名
static const char* password = "1234567890";        // WiFi 密碼

static const int water_pin    = 12;                // 澆水器的控制引腳,控制澆水器啟停
static const int sign_pin     = A0;                // 澆水器的狀態監視引腳,查看澆水器是否休眠
static const int monitor_pin  = 5;                 // 工作狀態監視引腳,監視澆水器啟停

QueueHandle_t Message_Queue;

static  int xTicksToDelay = 5000;                  // 周期延時時間

OneButton button(PIN_INPUT, true);

這里填寫設備的信息,方便在 Spirit 1 上查看和尋找你需要的設備:

/*
 *  當前設備的信息定義
 */
DEV_INFO    dev_info = {
            .name     = "智能澆水",                // 設備的名字
            .type     = "device",
            .excl     = SDDC_FALSE,
            .desc     = "ESP-32S",
            .model    = "1",
            .vendor   = "inspiration-desktop",
};

回調函數注冊

這是收到命令后回調函數注冊的位置,在這里注冊的函數才能被 SDK 正確的調用,執行正確的動作。

因為澆水器 set 命令為 string 類型,所以對應的處理函數 water_set 注冊到 IO設備對象設置函數與處理方法注冊 中。

/*
 * IO設備對象設置函數與處理方法注冊
 */
IO_DEV_REGINFO io_dev[] = {
        {"watering",water_set},
};

而 get 處理函數返回的同樣是 string 類型,所以在 系統對象狀態獲取注冊 中第二個參數選擇 DEV_IO_TYPE,並且注冊 get 處理函數 single_get_sensor。

/*
 *  系統對象狀態獲取注冊
 */
DEV_STATE_GET  dev_state_get_reg[] = {
        {"watering",   DEV_IO_TYPE,  single_get_sensor},
};

具體 SDK 的解析可以參考 同人逼死官方系列!基於sddc 協議的SDK框架 sddc_sdk_lib 解析同人逼死官方系列!從 DDC 嗅探器到 sddc_sdk_lib 的數據解析

數據獲取與發送流程

這里是自己編寫的處理流程 ,可以根據需求自己更改,收到 set 或者 get 后上文注冊的函數,進入對應的處理函數。

收到 set 命令后,通過關鍵字尋找到對應的處理函數 water_set ,判斷命令是否正確(比如說正在澆水的時候,收到一個ON命令),檢測澆水器是否休眠,如果休眠了那在觸發前就喚醒設備。

而收到 get 命令后進入對應的處理函數 single_get_sensor 通過讀取面板信號線判斷電機工作狀態,並且返回給 Spirit 1。

/* 
 *  主動數據上報函數
 */
static void report_sensor()
{  
    int sensorValue = 0;
    cJSON *value;
    cJSON *root;
     
    value =  cJSON_CreateArray();
    root = cJSON_CreateObject();
    sddc_return_if_fail(value);
    sddc_return_if_fail(root);
            
    // 按格式生成需要的數據
    cJSON_AddItemToArray(value, cJSON_CreateString("上報數據 1 "));    // 這里的字符串要和系統對象狀態獲取注冊結構體里的對應
    // cJSON_AddItemToArray(value, cJSON_CreateString("上報數據 2 ")); // 需要上報幾個就添加幾個  
    cJSON_AddItemToObject(root, "obj", value);
      
    // 發送數據給 EdgerOS
    object_report(root);
      
    cJSON_Delete(value);
}


/*
 * 澆水狀態監控函數
 */
static void monitor_task(void *arg)
{
    int newval = 1;
    int oldval = 1;

    // 監控澆水開啟和關閉狀態
    while(1)
    {
        newval = digitalRead(monitor_pin);
            
        if(newval != oldval)
        {
            report_sensor();
        } 
        oldval = newval;
        // 任務創建之后,設定延時周期
        delay(100);
    }
    vTaskDelete(NULL);
}

/*
 * 澆水觸發任務
 */
static void button_task(void *arg)
{
    char SW[5];
    char *value;
    BaseType_t err;                               
    
    if(Message_Queue != NULL)                             
    {               
        err = xQueueReceive(Message_Queue, &value, portMAX_DELAY );              
        if(err == pdFALSE)                           
        {                           
            printf("隊列 Message_Queue 數據獲取失敗!\r\n");                        
        }                           
    }
    sddc_printf("\nMessage_Queue value: %s!!!!!\n", value);

    // 監控電機工作狀態
    if(digitalRead(monitor_pin))
    {
        strcpy(SW,"OFF");
    }else
    {
        strcpy(SW,"ON");
    }
    // 如果命令要求與電機當前工作狀態一致就不處理
     if(0 != strcmp(value,SW) && (value != NULL))
     {
         // 判斷機器是否休眠如果休眠了就行喚醒機器
         delay(100);
         int a = analogRead(sign_pin);
         sddc_printf("\n a1 == : %d!!!!!\r\n", analogRead(sign_pin));

         if(!(a > 1) && (0 == strcmp(value,"ON")))
         {
             Serial.println("喚醒");
             digitalWrite(water_pin, HIGH);
             delay(100);
             digitalWrite(water_pin, LOW);
             delay(100);                          // 因為是下降沿觸發,所以加延遲保證下降沿不會被后面的命令沖掉
         }
      
         // 觸發澆水器開或者關
         Serial.println("觸發");
         digitalWrite(water_pin, HIGH);
         delay(100);
         digitalWrite(water_pin, LOW);
         delay(100);
     }
     vTaskDelete(NULL);
}

/*
 * 澆水器控制函數
 */
sddc_bool_t water_set(const char* value)
{
    BaseType_t err;                               
      
    sddc_printf("\niot_pi_on_message: %s!!!!!\n", value);

    if((Message_Queue != NULL)&&(value))                              
    {                             
        // 通過消息隊列儲存收到的命令,防止命令丟失
        err = xQueueSendToFront(Message_Queue,&value,0 );                            
        if(err == pdFALSE)                       
        {                           
            printf("隊列 Message_Queue 已滿,數據發送失敗!\r\n");                        
        }                           
    }

    // 創建電機觸發任務,防止阻塞message_ack回復
    xTaskCreate(button_task, "button_task", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
    return SDDC_TRUE;
}

/* 
 *  填寫澆水狀態
 */
sddc_bool_t single_get_sensor(char *objvalue, int value_len)     // 注意函數名要和上文注冊的函數名保持一致,當收到 get 消息之后通過關鍵字就能找到並且調用這個函數
{
    if(digitalRead(monitor_pin))
    {
        strncpy(objvalue, "OFF", value_len);
    }else
    {
        strncpy(objvalue, "ON", value_len);
    }
    return SDDC_TRUE;
}

代碼寫完之后燒錄進去就完事了,和之前完全一樣,點一下保存,然后上傳OK,具體可以看之前的文檔,我就懶得再寫一遍啦 (/ω\)。

總結

智能澆水器制作完成!加上之前制作的土壤濕度傳感器,和 Spirit 1 就完成了我們智能澆花場景的搭建。接下來就寫一個智能澆花的應用就能完美的解決忘記澆水的麻煩!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM