今天小編給大家帶來的是來自MIT的Jason的基于XIAO ESP32C6的HA空氣質量檢測儀項目,作為一名程序員,Jason經常自己連續幾個小時坐在辦公桌前,沉浸在一行行代碼中。后來意識到周圍的空氣質量,尤其是二氧化碳水平不斷上升。
制作背景
保持健康的工作空間至關重要,但我們需要一個既實用又美觀的解決方案。如果有一種緊湊型設備,不僅可以監測空氣質量,還可以作為美觀的辦公桌裝飾品,那不是很棒嗎?有了這個想法,Jason就開始著手實現它。Zigbee 是一種出色的智能家居低功耗通信協議。
使用 Seeed Studio 的 XIAO ESP32 C6 模塊作為主控。它擁有小巧的外形和全面的 Arduino Zigbee 教程為開發者節省了大量開發時間。此外,還增加了 XIAO 擴展板和 Grove VOC 和 eCO2 氣體傳感器 (SGP30) 以獲得準確的讀數。
由于不喜歡傳統的方形桌面擺件,所以Jason設計了一個小型站立機器人造型。至于細節,對 XIAO 擴展板的引腳排列進行了布線,以便在 XIAO 系列內的不同 MCU 之間輕松切換。這讓Jason在組件選擇上有了更大的靈活性。在結構件內,我將傳感器放在右側,用打印材料與MCU 隔開,確保最佳性能和時尚的設計。
材料清單
硬件列表
Grove-VOC and CO2 Gas Sensor SGP30
Seeed Studio XIAO ESP32C6Seeed Studio
XIAO Expansion Board
軟件列表
Arduino IDE
Autodesk Fusion
Home Assistant
項目演示
1.連接帶 OLED 顯示屏的設備
在 OLED 屏幕上,我們將顯示連接狀態,可以輕松查看 Zigbee 與 Home Assistant 的連接是否成功。此外,UI 設計中還會有一些小驚喜!
OLED 顯示屏將包含三個內容區域:
啟動 Zigbee 連接
連接成功狀態
CO2 和 eVOC 數據
我們還可以通過打開 Arduino 串行監視器來監視 XIAO ESP32 C6 的 Zigbee 連接狀態和數據輸出。
Arduino 串行監視器
2.HomeAssistant 帶二氧化碳傳感器界面效果截圖
將購買的 Home Assistant Connect ZBT-1 插入我的 HA 設置后,我通過 Zigbee Home Automation 添加了我們的 Zigbee 終端設備。隨后,在對 XIAO ESP32 C6 進行編程后,設備名稱出現在 OLED 顯示屏上。
HomeAssistant 查找 Zigbee 集成
HomeAssistant 連接 Zigbee 設備
訪問 Homeassistant 后,我們可以看到顯示的數據隨時間的變化
成功連接到 HomeAssistant 后,我們在 Zigbee 中找到了我們的設備,它提供了兩個主要功能:
實時數據顯示
查看歷史二氧化碳趨勢。值得注意的是,我的設備在此期間并未持續運行。
3.桌面上的最終設置
最后,我們可以看到 OLED 屏幕上顯示的傳感器數據,以及 Home Assistant 儀表板上的數據。
最終展示效果
#ifndef ZIGBEE_MODE_ED #error "Zigbee end device mode is not selected in Tools->Zigbee mode" #endif #include "Zigbee.h" #include "sensirion_common.h" #include "sgp30.h" #include#include U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); uint8_t HugoUI_Animation_EasyOut(float *a, float *a_trg, uint16_t n) { if (*a == *a_trg) return 0; float cz = fabs(*a - *a_trg); if (cz <= 1) *a = *a_trg; else { if (cz < 10) n = n * cz * 0.1f; if (n < 10) n = 10; *a += (*a_trg - *a) / (n * 0.1f); } return 1; } uint8_t HugoUI_Animation_EasyIn(float *a, float *a_trg, uint16_t n) { if (*a == *a_trg) return 0; float cz = fabs(*a - *a_trg); if (cz <= 1) *a = *a_trg; else if (cz > 20) n = n * 3; else if (cz > 15) n = n * 2; else if (cz > 5) n = n * 1; if (*a != *a_trg) *a += (*a_trg - *a) / (n * 0.1f); else return 0; return 1; } void Oled_DrawSlowBitmapResize(int x, int y, const uint8_t *bitmap, int w1, int h1, int w2, int h2) { uint8_t color = u8g2.getDrawColor(); float mw = (float)w2 / w1; float mh = (float)h2 / h1; uint8_t cmw = ceil(mw); uint8_t cmh = ceil(mh); int xi, yi, byteWidth = (w1 + 7) / 8; for (yi = 0; yi < h1; yi++) { for (xi = 0; xi < w1; xi++) { if (*(uint8_t *)(bitmap + yi * byteWidth + xi / 8) & (1 << (xi & 7))) { u8g2.drawBox(x + xi * mw, y + yi * mh, cmw, cmh); } else if (color != 2) { u8g2.setDrawColor(0); u8g2.drawBox(x + xi * mw, y + yi * mh, cmw, cmh); u8g2.setDrawColor(color); } } } } const unsigned char gImage_humidity[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x7C, 0x3F, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xF0, 0x01, 0xC0, 0x07, 0xE0, 0x01, 0xC0, 0x03, 0xC0, 0x03, 0xE0, 0x81, 0x81, 0x07, 0xE0, 0xC1, 0x83, 0x07, 0xE0, 0xD0, 0x03, 0x07, 0xF0, 0xF8, 0x03, 0x0F, 0xF0, 0xF8, 0x0B, 0x0F, 0xF0, 0xF0, 0x1F, 0x0F, 0xF0, 0xE0, 0x1F, 0x0F, 0xE0, 0xC0, 0x0F, 0x07, 0xE0, 0xC0, 0x07, 0x07, 0xE0, 0xC1, 0x83, 0x07, 0xC0, 0xC3, 0xC3, 0x03, 0xC0, 0xC7, 0xE3, 0x03, 0x80, 0x8F, 0xF3, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char gImage_homeassistant[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFC, 0xBF, 0x07, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x07, 0x80, 0xFF, 0xFF, 0x07, 0xC0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0x3F, 0xFE, 0xF8, 0x1F, 0x7F, 0x7F, 0xF7, 0xEF, 0xFE, 0x7F, 0xF7, 0xEF, 0xFE, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0xEF, 0xF7, 0x1F, 0xF8, 0xCF, 0xF3, 0x1F, 0xF8, 0x9F, 0xF9, 0x1F, 0xF8, 0x7F, 0xFE, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 32x32 #define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 10 uint8_t button = BOOT_PIN; ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER); int16_t err = 0; uint16_t tvoc_ppb, co2_eq_ppm; uint16_t carbon_dioxide_value; static uint32_t timeCounter = 0; static float img_a = 4, img_a_trg = 24; static float img_b = -2, img_b_trg = 24; static float img_c = -10, img_c_trg = 13; static float img_d = 5, img_d_trg = 90; static void carbon_sensor_update(void *arg) { for (;;) { if (!(timeCounter++ % 20)) { err = sgp_measure_iaq_blocking_read(&tvoc_ppb, &co2_eq_ppm); if (err == STATUS_OK) { Serial.printf("tVOC Concentration: %d ppb ", tvoc_ppb); Serial.printf("CO2eq Concentration: %d ppm ", co2_eq_ppm); carbon_dioxide_value = co2_eq_ppm; zbCarbonDioxideSensor.setCarbonDioxide(carbon_dioxide_value); } else { Serial.println("Error reading IAQ values "); } zbCarbonDioxideSensor.report(); delay(6000); } } } void setup() { int16_t err; uint16_t scaled_ethanol_signal, scaled_h2_signal; Serial.begin(115200); u8g2.begin(); // Init RF pinMode(WIFI_ENABLE, OUTPUT); digitalWrite(WIFI_ENABLE, LOW); delay(100); pinMode(WIFI_ANT_CONFIG, OUTPUT); digitalWrite(WIFI_ANT_CONFIG, LOW); // Init button switch pinMode(button, INPUT_PULLUP); // Init SGP30 while (sgp_probe() != STATUS_OK) { Serial.println("SGP failed"); while (1) ; } err = sgp_measure_signals_blocking_read(&scaled_ethanol_signal, &scaled_h2_signal); if (err == STATUS_OK) { Serial.println("get ram signal!"); } else { Serial.println("error reading signals"); } err = sgp_iaq_init(); zbCarbonDioxideSensor.setManufacturerAndModel("Espressif", "ZigbeeCarbonDioxideSensor"); zbCarbonDioxideSensor.setMinMaxValue(0, 1500); Zigbee.addEndpoint(&zbCarbonDioxideSensor); Serial.println("Starting Zigbee..."); u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.drawStr(0, 30, "Connecting to Zigbee..."); u8g2.sendBuffer(); if (!Zigbee.begin()) { Serial.println("Zigbee failed to start!"); Serial.println("Rebooting..."); ESP.restart(); } else { Serial.println("Zigbee started successfully!"); } Serial.println("Connecting to network"); while (!Zigbee.connected()) { Serial.print("."); delay(100); } u8g2.clearBuffer(); u8g2.drawStr(0, 30, "Successfully connect"); u8g2.drawStr(0, 50, "Zigbee network!"); u8g2.sendBuffer(); Serial.println(); delay(5000); // Start carbon sensor reading task xTaskCreate(carbon_sensor_update, "carbon_sensor_update", 2048, NULL, 10, NULL); zbCarbonDioxideSensor.setReporting(0, 30, 0); } void loop() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.drawXBM(0, 0, 32, 32, gImage_homeassistant); u8g2.drawStr(43, img_b, " Air Monitor"); u8g2.setDrawColor(2); u8g2.drawRBox(36, img_c, img_d, 15, 1); u8g2.setDrawColor(1); u8g2.drawStr(0, 45, "CO2: "); u8g2.setCursor(30, 45); u8g2.print(carbon_dioxide_value); u8g2.drawStr(55, 45, "ppb"); u8g2.drawStr(0, 60, "TVOC: "); u8g2.setCursor(38, 60); u8g2.print(tvoc_ppb); u8g2.drawStr(55, 60, "ppm"); if (img_a == img_a_trg) { if (img_a == 4) { img_a_trg = 24; } else if (img_a == 24) img_a_trg = 4; } HugoUI_Animation_EasyOut(&img_b, &img_b_trg, 100); HugoUI_Animation_EasyIn(&img_a, &img_a_trg, 115); HugoUI_Animation_EasyOut(&img_c, &img_c_trg, 100); HugoUI_Animation_EasyOut(&img_d, &img_d_trg, 100); Oled_DrawSlowBitmapResize(118 - img_a / 2, 50 - img_a / 4, gImage_humidity, 32, 32, img_a, img_a); u8g2.sendBuffer(); if (digitalRead(button) == LOW) { delay(100); int startTime = millis(); while (digitalRead(button) == LOW) { delay(50); if ((millis() - startTime) > 3000) { Serial.println("Resetting Zigbee to factory and rebooting in 1s."); delay(1000); Zigbee.factoryReset(); } } } }
改進計劃
得益于 Seeed Studio XIAO 擴展板提供的眾多 Grove 接口,Jason計劃將其他傳感器集成到這個機器人中來收集更多數據。這將可以通過自動化設備來控制其他的智能家居設備,例如當空氣太干燥時啟動除濕機,或者當二氧化碳水平上升時運行空氣循環裝置。總而言之,Jason說這個智能二氧化碳監測器項目是一次受益匪淺的創新和學習之旅。通過將技術與設計相結合,他創造了一種不僅可以跟蹤空氣質量還可以增強工作空間美感的設備。接下來他會逐步利用 Zigbee 連接不同的傳感器,打造智能家居生態系統。
-
傳感器
+關注
關注
2563文章
52575瀏覽量
763733 -
mcu
+關注
關注
146文章
17838瀏覽量
360444 -
機器人
+關注
關注
213文章
29493瀏覽量
211558 -
空氣質量檢測儀
+關注
關注
0文章
11瀏覽量
1298
原文標題:創客項目秀|基于XIAO ESP32C6的HA空氣質量檢測儀
文章出處:【微信號:ChaiHuoMakerSpace,微信公眾號:柴火創客空間】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
【GoKit申請】空氣質量檢測儀
Pm2.5空氣質量檢測的小問題
空氣質量測試儀的原理
德爾格壓縮空氣質量檢測儀6種型號的詳細介紹
空氣質量檢測儀的簡單介紹
空氣質量檢測儀的特點介紹
網格化空氣質量監測站的特點
空氣質量檢測儀使用方法是怎樣的
空氣質量檢測儀優勢特點介紹
空氣質量檢測儀原理與應用介紹
ONETEST-100AQL空氣質量檢測儀工作原理介紹

評論