從零開始 DIY 智慧家居 - 基於 ESP32 的智慧紫外線感測器模組

靈感桌面發表於2021-11-01

前言

做了這麼多感測器都是自己玩,這次家裡人看不下去了,非得讓我整一個紫外線感測器,出門前看看紫外線強度(嚴正宣告:我不是個單身狗!!!(¬◡¬)✧)我看了一下網上還真有合適的模組,於是就整了一個。這次我們就來做一個智慧紫外線感測器。

硬體選擇

萬能不變,便宜好用的(其實最近發現好像也沒這麼好用,但是確實便宜)的安信可 ESP32S

紫外線感測器選擇的是微雪的數字UV紫外線感測器 UV Sensor C,42塊錢有點小貴QWQ,但是可以直接獲取紫外線強度,不需要自己用模擬量算了,響應波長在280-430nm,官方資料也很完善齊全。
在這裡插入圖片描述

以及我們裝置的外接思考迴路 - 翼輝 Spirit 1 邊緣計算機。

二、使用步驟

獲取程式碼

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

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

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

在這裡插入圖片描述

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

libsddc:是我移植自官方的SDDC庫和自己寫的 SDK,也是放入 libraries 資料夾裡就行。裡面是 SDDC 協議的處理函式,我們不用管。
Waveshare_UV_C: 是微雪官方提供的語音播報模組的庫,不過這個庫有一點點不同,是給 AVR 架構的晶片做的,編譯的時候會彈出一個警告:
在這裡插入圖片描述
不過我看了一下庫裡面的內容,除了一個好像沒有用上的 INT_PIN 定義,其他的應該都能正常使用。
另外在載入庫的時候需要選擇 Waveshare:
在這裡插入圖片描述
demo 資料夾裡面就是我們各種感測器的 demo 程式碼了:
在這裡插入圖片描述
紅圈的 LTR390UV_sddc_sdk_demo資料夾裡面就是我們程式碼,點進去就能看見 LTR390UV_sddc_sdk_demo.ino 檔案,雙擊檔案會自動啟動 arduino-IDE 開啟程式碼。在工具 -> 埠 選擇對應的 COM 口然後點選上傳就可以把程式碼燒錄到板子裡:
在這裡插入圖片描述
具體 arduino 使用教程可以看我之前的文章 arduino開發指導手把手帶你 arduino 開發:基於ESP32S 的第一個應用-紅外測溫槍(帶引腳圖)

裝置控制命令:

通過 Spirit 1 的應用程式或者嗅探器 向感測器裝置傳送的命令:
獲取紫外線強度資料

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

裝置和協議初始化流程:

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

/*
 * 初始化感測器
 */
void sensor_init()
{
    // 初始化I2C
    if(LTR390_Init() != 0)
    {
        Serial.print("LTR390 init err!!!");
        while(1);
    }
    LTR390_SetIntVal(5, 20);
    pinMode(INT_OUT, INPUT);
    // 建立感測器任務,週期性感測器的資料併傳送給 EdgerOS
    xTaskCreate(UV_sensor_task, "UV_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 SDDC_CFG_PORT             680U             // SDDC 協議使用的埠號
#define PIN_INPUT                 0                // 選擇 IO0 進行控制
#define ESP_TASK_STACK_SIZE       4096
#define ESP_TASK_PRIO             25
#define INT_OUT                   14               // 選擇 IO14 中斷輸出

static const char* ssid = "EOS-Tenda";             // WiFi 名
static const char* password = "1234567890";        // WiFi 密碼
static double UV;
OneButton button(PIN_INPUT, true);


/*
 *  當前裝置的資訊定義
 */
DEV_INFO    dev_info = {
            .name     = "紫外線感器",
            .type     = "device.UV",
            .excl     = SDDC_FALSE,
            .desc     = "ESP-32S + LTR390UV",
            .model    = "IDUV01B",
            .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[] = {
//        {"set_num_demo", demo},                          // 字串為輸入命令,demo為命令處理函式
};

/*
 *  顯示裝置物件函式與處理方法註冊
 */
DIS_DEV_REGINFO dis_dev[] = {
//        {"set_dis_demo", demo},                          // 字串為輸入命令,demo為命令處理函式
};

/*
 * IO裝置物件設定函式與處理方法註冊
 */
IO_DEV_REGINFO io_dev[] = {
//        {"set_io_demo", demo},                           // 字串為輸入命令,demo為命令處理函式
};

/*
 *  系統物件狀態獲取註冊
 */
DEV_STATE_GET  dev_state_get_reg[] = {
    {"UV",   DEV_NUM_TYPE,  single_get_sensor},
//        {"demo",   DEV_NUM_TYPE,  num_get_demo},         // demo為輸入命令,字串為命令處理函式
//        {"demo",   DEV_IO_TYPE,  io_get_demo},
//        {"demo", DEV_DISPLAY_TYPE, dis_get_demo},

};

資料獲取與上報流程

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

static void UV_sensor_task(void *arg)
{  
    double val = 0;
    while(1)
    {
        val = LTR390_UVS();
        UV = val;
        if(digitalRead(8) == 1){
        Serial.print("UV = ");
        Serial.println(UV);
  }
        delay(300);
    }
}
/* 
 *  單次獲取資料
 */
sddc_bool_t single_get_sensor(char *objvalue, int value_len)
{
    double value = UV;
    snprintf(objvalue, value_len, "%lf", value);
    return SDDC_TRUE;
}

總結

剛才拿去試了一下,外面紫外線強度居然有9!我查了一下:

當紫外線為最弱(0 ~ 2級)時對人體無太大影響,外出時戴上太陽帽即可;紫外線達3 ~ 4級時,外出時除戴上太陽帽外還需備太陽鏡,並在身上塗上防曬霜,以避免皮膚受到太陽輻射的危害;當紫外線強度達到5 ~ 6級時,外出時必須在陰涼處行走;紫外線達7 ~ 9級時,在上午10時至下午4時這段時間最好不要到沙灘場地上曬太陽;當紫外線指數大於等於10時,應儘量避免外出,因為此時的紫外線輻射極具有傷害性。

以前完全沒有注意過!沒到我們這紫外線強度這麼高!

相關文章