1. 程式人生 > 其它 >從零開始的DIY智慧家居 - 基於 ESP32 的智慧澆水器

從零開始的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 就完成了我們智慧澆花場景的搭建。接下來就寫一個智慧澆花的應用就能完美的解決忘記澆水的麻煩!