從零開始的DIY智能家居 - 基於 ESP32 的智能光照傳感器


前言

上周出差有點急,結果家里燈沒關,開了整整一周的時間(T▽T),整個人都裂開了,准備做一個能夠遠程控制燈的東西,讓我以后出差能遠程把家里燈關了。

第一步就是做這期的主題 - 智能光照傳感器,因為我逛了一圈發現常見的能遠程控燈的開關都只能單純的開關燈的狀態,並不知道當前燈的狀態,小米智能燈泡又只能用他們自己的生態,我用不了。

於是我准備使用一個智能光照傳感器來感知家里的燈有沒有關。

硬件選擇

板子依然用的安信可的 ESP32S ,別問,問就是便宜好用。STM完蛋去吧。

傳感器用的 HB1750VI模塊 ,這個模塊使用 I2C 控制和通信(連接開發板:SDA->P21,SCL->P22)
在這里插入圖片描述

服務器用的翼輝的 Spirit 1 邊緣計算機

Spirit 1 這就是一個前后端部署在一起的服務器,然后我們手機和電腦就相當於一個遠程桌面。

代碼解析

獲取代碼

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

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

下載或者 clone代碼后這次用到的是這個三個文件夾:

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

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

demo 文件夾里面就是我們各種傳感器的 demo 代碼了:
在這里插入圖片描述

紅圈的 MLX90614_sddc_demo 文件夾里面就是我們代碼,點進去就能看見 MLX90614_sddc_sdk_demo.ino 文件,雙擊文件會自動啟動 arduino-IDE 打開代碼。在工具 -> 端口 選擇對應的 COM 口然后點擊上傳就可以把代碼燒錄到板子里:
在這里插入圖片描述

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

設備控制命令:

通過 Spirit 1 的應用程序或者嗅探器 向傳感器設備發送的命令:

{
  "method": "get",        // 這個命令可以從傳感器主動獲取一個光照強度數據
  "obj": ["LUX"]
}

設備和協議初始化流程:

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

/*
 * 初始化傳感器
 */
void sensor_init()
{
    // 創建傳感器任務,周期性獲取光照傳感器的數據並發送給 EdgerOS
    xTaskCreate(periodic_sensor_task, "periodic_sensor_task", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
}

void setup() {
    byte mac[6];
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    Serial.println();

    // 初始化傳感器
    sensor_init();

    // 清除一下按鍵狀態機的狀態
    button.reset();
  
    // 創建按鍵掃描線程,長按 IO0 按鍵,松開后 ESP32 將會進入 SmartConfig 模式
    sddc_printf("長按按鍵進入 Smartconfig...\n");
    button.attachLongPressStop(esp_io0_key_task);
    xTaskCreate(esp_tick_task, "button_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
    
    // 啟動 WiFi 並且連接網絡
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
    {
        delay(500);
        Serial.print(".");
    }
  
    // 獲取並打印 IP 地址
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("'ip :");
    Serial.print(WiFi.localIP());
    Serial.println("' to connect"); 
  
    // sddc協議初始化
    sddc_lib_main(&sys_cfg);

    // 獲取並打印網卡 mac 地址
    WiFi.macAddress(mac);
    sddc_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
              mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
    // 使用網卡 mac 地址設置設備唯一標識 UID
    sddc_set_uid(G_sddc, mac);
}

void loop() {
    // 運行 SDDC 協議循環
    while (1) 
    {
        sddc_printf("SDDC running...\n");
        sddc_run(G_sddc);
        sddc_printf("SDDC quit!\n");
    }

    // 銷毀 SDDC 協議
    sddc_destroy(G_sddc);
}

配置設備信息

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


#define ADDR 0b0100011

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

static const char* ssid = "EOS-000045";            // WiFi 名
static const char* password = "1234567890";        // WiFi 密碼

OneButton button(PIN_INPUT, true);

/*
 *  當前設備的信息定義
 */
DEV_INFO    dev_info = {
            .name     = "光照傳感器",
            .type     = "sensor",
            .excl     = SDDC_FALSE,
            .desc     = "ESP-32S",
            .model    = "1",
            .vendor   = "靈感桌面",
};

/*
 *   系統注冊對象匯聚
 */
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[] = {

};

/*
 *  顯示設備對象函數與處理方法注冊
 */
DIS_DEV_REGINFO dis_dev[] = {

};

/*
 * IO設備對象設置函數與處理方法注冊
 */
IO_DEV_REGINFO io_dev[] = {

};

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

數據獲取與發送流程

這里是我們自己編寫的處理流程 ,可以根據你的需求自己更改,收到 set 或者 get 后根據前面的注冊的函數,進入對應的處理函數。
設備會檢測傳感器輸出,然后根據設置的上報間隔定時上報水濁度數據,還可以主動發送 get 命令主動查詢傳感器當前數據:

static int esp_sensor_task()
{  
    int val = 0;
    // 獲取光照數據
    Wire.beginTransmission(ADDR);
    Wire.write(0b00000111);
    Wire.endTransmission();
 
    Wire.beginTransmission(ADDR);
    Wire.write(0b00100000);
    Wire.endTransmission();
    // typical read delay 120ms
    delay(120);
    Wire.requestFrom(ADDR, 2); // 2byte every time
    for (val = 0; Wire.available() >= 1; ) 
    {
      	char c = Wire.read();
      	val = (val << 8) + (c & 0xFF);
    }
    val = val / 1.2;    

    return val;
}
/* 
 *  單次獲取數據
 */
sddc_bool_t single_get_sensor(char *objvalue, int value_len)
{
    int value = esp_sensor_task();
    snprintf(objvalue, value_len, "%d", value);
    return SDDC_TRUE;
}

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


總結

智能光照傳感器完成!現在能通過感知光照感知白天,黑夜,是否開關燈了!之后准備買一個隨意貼來改造,讓我出差也能遠程關燈關設備!

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


免責聲明!

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



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