1. 程式人生 > 其它 >從零開始 DIY 智慧家居 - 智慧開窗器

從零開始 DIY 智慧家居 - 智慧開窗器

前言

做完智慧澆水器之後對這種可以節省時間和精力的場景總有一種謎之嚮往(懶鬼是這樣的),這次我準備做一個可以自動開窗的裝置,結合之前的甲醛檢測感測器就可以實現甲醛含量過高自動開窗通風,之後還可以把燃氣檢測、煙霧檢測、一氧化碳檢測、空氣質量檢測的各種氣體感測器也接入進來,這個場景的覆蓋面以及實用性也拉滿了,有朋友要是還有想法可以在這個場景中接入哪些部件也可以留言給我,之後我都接入進來(快給我留言吧!求求你們了,讓我室友給大夥磕頭了!)。

硬體選擇

板子依然選擇超級好用,無敵便宜,資料齊全,生態完善之安信可 ESP32S

電動開窗器的選擇呢,因為我家是常見的滑動平移的窗戶,淘寶上大多都是推拉的,找了半天挑了一家最便宜的進行改造 ,別問,問就是窮!(T▽T)

接線有些複雜,因為這個開窗器的方向是由電流方向控制的,我就用了手邊的一個三路繼電器做了個電流反轉的功能,接線具體如下:

ESP32S-5V --- 繼電器-Vcc

ESP32S-GND --- 繼電器-GND

ESP32S-P25 --- 繼電器-1路控制端

ESP32S-P26 --- 繼電器-2路控制端

ESP32S-P27 --- 繼電器-3路控制端

+24V 輸出 --- 繼電器1路中間節點

-24V 輸出 --- 繼電器3路中間節點

繼電器1路常開節點 --- 繼電器2路中間節點

繼電器2路常閉節點 --- 繼電器3路常開節點

繼電器2路常開節點 --- 繼電器3路常閉節點

繼電器3路常閉節點 --- 開窗器的 +24V 輸入

繼電器3路常開節點 --- 開窗器的 - 24V 輸入

程式碼解析

獲取程式碼

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

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

下載或者 clone程式碼後這次用到的是這個三個資料夾:


cjson:我移植的 cjson 庫,就是標準的 cjson 庫,放到 arduino 安裝目錄下的 libraries 資料夾裡,百度一下 cjson 的函式使用就行了。

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

demo 資料夾裡面就是我們各種感測器的 demo 程式碼了:

紅圈的 windows_sddc_sdk_demo 資料夾裡面就是我們程式碼,點進去就能看見 windows_sddc_sdk_demo.ino 檔案,雙擊檔案會自動啟動 arduino-IDE 開啟程式碼。在工具 -> 埠 選擇對應的 COM 口然後點選上傳就可以把程式碼燒錄到板子裡:

具體 arduino 使用教程可以看我之前的文章 arduino開發指導手把手帶你 arduino 開發:基於ESP32S 的第一個應用-紅外測溫槍(帶引腳圖)

裝置控制命令:

通過 Spirit 1 的應用程式或者嗅探器 向感測器裝置傳送的命令:

獲取窗戶當前位置百分比

{
  "method": "get",
  "obj": ["window_percent"]
}

向左移動

{
  "method": "set",
  "window": "left"
}

向右移動

{
  "method": "set",
  "window": "right"
}

停止移動

{
  "method": "set",
  "window": "OFF"
}

配置裝置資訊

這部分程式碼可以配置 WiFi 名字和 WiFi 密碼,要使用的引腳,並且配置裝置在 Spirit 1 上顯示的資訊:

#define SDDC_CFG_PORT             680U             // SDDC 協議使用的埠號
#define PIN_INPUT                 0                // 選擇 IO0 進行控制
#define ESP_TASK_STACK_SIZE       4096
#define ESP_TASK_PRIO             25

#define WINDOW_STATE_RIGHT        1
#define WINDOW_STATE_LEFT         0
#define WINDOW_ON                 1
#define WINDOW_OFF                0
#define WINDOW_PERCENT            "window_percent"


static int window_state           = 0;
static int window_direction       = 0;
static double window_percent      = 0;
static sddc_t *g_sddc;
static const char* ssid           = "EOS-Tenda";         // WiFi 名
static const char* password       = "1234567890";        // WiFi 密碼

static const int window2_pin      = 25;                 // 電機方向的控制引腳
static const int window1_pin      = 26;                 // 電機方向的控制引腳
static const int off_pin          = 27;                 // 電機啟停的控制引腳

/*
 *  當前裝置的資訊定義
 */
DEV_INFO    dev_info = {
            .name     = "智慧開窗器",
            .type     = "device.window",
            .excl     = SDDC_FALSE,
            .desc     = "ESP-32S",
            .model    = "IDWINDOW01B",
            .vendor   = "inspiration-desktop",
};

/*
 *   系統註冊物件匯聚
 */
SDDC_CONFIG_INFO sys_cfg = {
        .token             = "1234567890",            // 裝置密碼
        .devinfo           = &dev_info,               
        .io_dev_reg        = io_dev,
        .io_dev_reg_num    = ARRAY_SIZE(io_dev),
        .num_dev_reg       = num_dev,
        .num_dev_reg_num   = ARRAY_SIZE(num_dev),
        .state_get_reg     = dev_state_get_reg,
        .state_get_reg_num = ARRAY_SIZE(dev_state_get_reg),
        .dis_dev_reg       = dis_dev,
        .dis_dev_num       = ARRAY_SIZE(dis_dev),
};

回撥函式註冊

這是收到命令後回撥函式註冊的位置,在這裡註冊的函式才能被 SDK 正確的呼叫,執行正確的動作。

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

/* 
 *  數字量裝置物件函式與處理方法註冊
 */
NUM_DEV_REGINFO num_dev[] = {
        {"window_percent",window_percent_set},
};

/*
 *  顯示裝置物件函式與處理方法註冊
 */
DIS_DEV_REGINFO dis_dev[] = {
};

/*
 * IO裝置物件設定函式與處理方法註冊
 */
IO_DEV_REGINFO io_dev[] = {
        {"window",window_set},
};

/*
 *  系統物件狀態獲取註冊
 */
DEV_STATE_GET  dev_state_get_reg[] = {
        {"window",   DEV_IO_TYPE,  window_get},
        {"window_percent", DEV_NUM_TYPE, window_percent_get},
};

資料獲取與傳送流程

這裡是我們自己編寫的處理流程 ,可以根據你的需求自己更改,收到 set 或者 get 後根據前面的註冊的函式,進入對應的處理函式。

static void dev_report_state()
{
    cJSON * root = cJSON_CreateObject();
    cJSON * array = cJSON_CreateArray();
    cJSON_AddItemToArray(array, cJSON_CreateString(WINDOW_PERCENT));
    cJSON_AddItemToObject(root, "obj", array);
    object_report(root);
}

static void window_percent_task(void *arg) {
    while(1) {
        if (window_state) {
            if (window_direction == 1 && window_percent < 100) {
                window_percent++;
            } else if (window_direction == 0 && window_percent > 0) {
                window_percent--;
            }
            if (window_percent == 0 || window_percent == 100) {
                printf("停止移動!\n");
                digitalWrite(off_pin, LOW);
                window_state = WINDOW_OFF;
            }
            dev_report_state();
        }
        delay(250);
    }
}

sddc_bool_t window_percent_set(const uint64_t value)
{
    window_percent = value;
    return SDDC_TRUE;
}

/*
 * 開窗控制函式
 */
sddc_bool_t window_set(const char* value)
{
    if (!strcmp(value,"left") && window_percent > 0) {
        digitalWrite(off_pin, LOW);
        digitalWrite(window1_pin, HIGH);
        digitalWrite(window2_pin, HIGH);
        window_direction = WINDOW_STATE_LEFT;
        window_state = WINDOW_ON;
        if (digitalRead(window1_pin) == 1 && digitalRead(window2_pin) == 1) {
            digitalWrite(off_pin, HIGH);
            printf("向左移動!\n");
        } else {
            printf("短路警告!!!\n");
            digitalWrite(off_pin, LOW);
        }
    } else if (!strcmp(value,"right") && window_percent < 100) {
        digitalWrite(off_pin, LOW);
        digitalWrite(window1_pin, LOW);
        digitalWrite(window2_pin, LOW);
        window_direction = WINDOW_STATE_RIGHT;
        window_state = WINDOW_ON;
        if (digitalRead(window1_pin) == 0 && digitalRead(window2_pin) == 0) {
            digitalWrite(off_pin, HIGH);
            printf("向右移動!\n");
        } else {
            printf("短路警告!!!\n");
            digitalWrite(off_pin, LOW);
        }
    } else if (!strcmp(value,"OFF")) {
        printf("停止移動!\n");
        digitalWrite(off_pin, LOW);
        window_state = WINDOW_OFF;
    } else if (!strcmp(value,"init")) {
        printf("init!\n");
    }
    return SDDC_TRUE;
}

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

驗證

開啟之前寫的 DDC 協議嗅探器進行下測試,沒毛病,之後再寫個應用,完美!

總結

這個智慧開窗器改完還是蠻有成就感的(主要是那個接線太炸裂了〒▽〒),之後會寫個應用來把這個場景完善的表現出來,大家敬請期待吧,有什麼好的想法和建議也可以給我評論留言呦!

本文僅個人學習使用,如有錯誤,歡迎指正, ( ੭ ˙ᗜ˙ )੭謝謝老闆!