從零開始的DIY智慧家居 - 基於 ESP32 的智慧水濁度感測器
前言
家裡有個魚缸養了幾條魚來玩玩,但是換水的問題著實頭疼,經常一個不注意就忘記換水,魚兒就沒了。o(╥﹏╥)o
在獲得 Spirit 1 邊緣計算機 後就相當於有了一個人智慧裝置伺服器,可以自己開發在家裡開發智慧裝置,於是準備做一個智慧水濁度感測器來解決一下我這個換水難的問題。
目前的想法就是看看水啥時候改換了提醒我一下手動換水,自動換水過濾的裝置太貴了 ,窮逼只能看看怎麼 DIY 一套。
硬體選擇
這次依然還是用著安信可的 ESP32S ,別問,問就是便宜,至於那個 IOT PI ?已經被我做成智慧甲醛檢測器 塞櫃子裡面去了 ,有興趣的朋友可以去看看哦!把 IOT PI 換成ESP32S 成本也就60塊錢。
感測器用的 DFrboot 的
伺服器用的翼輝的 Spirit 1 。
我是不是該讓安信可 給我廣告費啊?天天用他家板子 ̄ω ̄=,我選擇安信可的具體原因可以看arduino開發指導 和 手把手帶你 arduino 開發:基於ESP32S 的第一個應用-紅外測溫槍(帶引腳圖) 裡面還有很詳細的 arduino 入門教程。
感測器接線:使用 A0 控制(SVP/IO36),電源接5V。
程式碼解析
獲取程式碼
為了方便講解邏輯,我會打亂程式碼的順序可能還會進行裁剪,要是想直接拿程式碼跑的朋友可以直接去 靈感桌面的祕密寶庫 獲取程式碼,或者直接 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 程式碼了:
紅圈的 TurbidityA_sddc_demo 資料夾裡面就是我們程式碼,點進去就能看見 TurbidityA_sddc_sdk_demo.ino 檔案,雙擊檔案會自動啟動 arduino-IDE 開啟程式碼。在工具 -> 埠 選擇對應的 COM 口然後點選上傳就可以把程式碼燒錄到板子裡:
具體 arduino 使用教程可以看我之前的文章
裝置控制命令:
通過 Spirit 1 的應用程式或者嗅探器 向感測器裝置傳送的命令:
{
"method": "get", // 這個命令可以讓感測器主動傳送一個當前水濁度
"obj": ["turbidity"]
}
{
"method": "set", // 這個命令可以調整感測器主動上報的時間間隔,水濁度變化應該不會很快,可以設定慢一些
"periodic_time": 1000
}
裝置和協議初始化流程:
基於官方 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 上顯示的資訊:
#include "Arduino.h"
#include <OneButton.h>
#include <WiFi.h>
#include <SDDC_SDK_lib.h>
#include <cJSON.h>
#include <Wire.h>
#define SDDC_CFG_PORT 680U // SDDC 協議使用的埠號
#define PIN_INPUT 0 // 選擇 IO0 進行控制
#define ESP_TASK_STACK_SIZE 4096
#define ESP_TASK_PRIO 25
static const int sensor_in = 34; // 資料輸入引腳
static const char* ssid = "EOS-000045"; // WiFi 名
static const char* password = "1234567890"; // WiFi 密碼
static int xTicksToDelay = 100; // 週期延時時間
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[] = {
{"periodic_time",periodic_time_set}, // 當接收到的命令中有 periodic_time 時,就會執行 periodic_time_set 函式
};
/*
* 顯示裝置物件函式與處理方法註冊
*/
DIS_DEV_REGINFO dis_dev[] = {
};
/*
* IO裝置物件設定函式與處理方法註冊
*/
IO_DEV_REGINFO io_dev[] = {
};
/*
* 系統物件狀態獲取註冊
*/
DEV_STATE_GET dev_state_get_reg[] = {
{"turbidity", DEV_NUM_TYPE, single_get_sensor},
};
資料獲取與傳送流程
這裡是我們自己編寫的處理流程 ,可以根據你的需求自己更改,收到 set 或者 get 後根據前面的註冊的函式,進入對應的處理函式。
裝置會檢測感測器輸出,然後根據設定的上報間隔定時上報水濁度資料,還可以主動傳送 get 命令主動查詢感測器當前資料:
/*
* 週期上報函式
*/
static void periodic_sensor_task(void *arg)
{
while(1)
{
// 任務建立之後,設定延時週期
printf("延時時間:%d",xTicksToDelay);
delay(xTicksToDelay);
get_sensor();
delay(100);
}
// 已停止傳送資料
Serial.printf("Soil humidity data OFF\n");
}
/*
* 主動資料上報函式
*/
static void report_sensor()
{
int sensorValue = 0;
cJSON *value;
cJSON *root;
char *msg;
value = cJSON_CreateArray();
root = cJSON_CreateObject();
sddc_return_if_fail(value);
sddc_return_if_fail(root);
sddc_return_if_fail(value);
// 組裝上報報文
cJSON_AddItemToArray(value, cJSON_CreateString("turbidity"));
cJSON_AddItemToObject(root, "obj", value);
// 將組裝好的報文傳給上報函式
msg = cJSON_Print(root);
printf("定時上報: %s\n",msg);
object_report(root);
cJSON_Delete(value);
cJSON_free(msg);
}
/*
* 設定週期等待時間
*/
sddc_bool_t periodic_time_set(const uint64_t value)
{
printf("修改定時時間!\n");
xTicksToDelay = value;
return SDDC_TRUE;
}
/*
* 單次獲取資料
*/
sddc_bool_t single_get_sensor(char *objvalue, int value_len)
{
int sensorValue = analogRead(A0); //put Sensor insert into soil
int value = sensorValue * (5.0 / 1024.0); // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
snprintf(objvalue, value_len, "%d", value);
return SDDC_TRUE;
}
程式碼寫完之後燒錄進去就完事了,和之前完全一樣,點一下儲存,然後上傳OK,具體可以看之前的文件,我就懶得再寫一遍啦 (/ω\)
總結
感測器是做完了,但是emmmm養魚換水的渾濁度範圍是多少啊?做完我才反應過來〒▽〒。之後有時間記錄一下我換水的時候的渾濁度,然後在前端做判斷吧
本文僅個人學習使用,如有錯誤,歡迎指正, ( ੭ ˙ᗜ˙ )੭謝謝老闆!