手搓空气检测仪

前言

新家打算做个骚一点的全屋智能,打算全部尽量自己手搓。由于距离开始装修还有挺长一段时间,手有点痒,再加上之前买的青萍的空气检测仪开不了机了,于是决定自己动手做一个可以检测多种空气指标的检测仪。

先来成品图~(没错,这玩意其实4月份就做出来了,博客愣是被我拖了大半年hhh)

pic1

需求整理

首先,要有一个大屏幕,用来显示以下数据:

  1. 温度
  2. 湿度
  3. 二氧化碳
  4. tVOC
  5. 综合污染指数
  6. 光照强度
  7. 日期和时间

传感器塞到本体里面,可能的尽量控制以下体积,不过考虑到我买的传感器都是一些模块,模块和主板之间会有很多杜邦线连接,体积估计小不到哪去。。

本来打算弄个甲醛传感器的,到淘宝搜了一波发现好像都有点太贵了,就买了SGP30二氧化碳和tVOC传感器,MQ135综合空气质量传感器(买它纯粹是因为便宜XD),TEMT6000光照传感器,DHT22温湿度传感器

硬件

硬件选型

外壳:3D打印制作,使用502胶和螺丝连接各个部分
屏幕:淘晶驰的串口屏,选的最便宜的T1系列,4.3寸屏幕,电阻触摸(虽说最终没用上触摸功能)

pic2

主板:自己设计PCB,主控芯片为ESP32-WROOM-32,主板上的其他电路主要就是降压(AMS1117)和提供传感器的插座

pic3

温湿度传感器:DHT22,之前有个DHT11,觉得精度有点差,这次就买了DHT22,价格差距也不大

pic4

综合空气质量传感器:MQ135,便宜大碗,说是对氨气,硫化物,苯系蒸汽灵敏度高,这个我也没法查证

pic5

二氧化碳和tVOC传感器:SGP30,打算放到办公室用,有点担心室内的CO2和甲醛,就花重金买了这个传感器(tVOC浓度在一定程度上可以体现甲醛浓度)

pic6

光照传感器:TEMT6000,支持测量 1~1000 lux的光照强度

pic7

散热:机身里面塞这么多东西,并且MQ135和SGP30在工作的时候都是要发热的,肯定会影响到DHT22的测量,所以得给机器加个风扇,我用的是笔记本用的那种涡轮风扇

pic8

PCB设计和制作

在B站找了个嘉立创快速入门的课,现学现卖。可惜大学学的电路知识都已经还给老师了,只能一边做一边学了。原理图很简单,如下:

pic9

主板的功能之一是把输入的5V电压转成3.3V,给ESP32模组供电。另外各个传感器需要的输入电压也是5V和3.3V都有。

其他功能就是给各种传感器,加上屏幕和风扇提供插座了。最终成品渲染图如下:

pic10

3D打印外壳

去年双十一购入了一台3D打印机,之前只是自己在网上找些模型来打印,现在为了这个项目,终于要打印自己制作的模型了。

于是找了一波教程简单学了下Solid Works,就马上开始上手,边做边查,果然还是遇到了不少问题,其中最重要也是最频繁出现的就是零件之间的连接问题。怎么设计尺寸来适配FDM打印机的特性,才能让两个零件可以不紧不松的插接起来;我想通过螺丝来固定两个零件(比如前壳和后盖),怎么设计螺丝孔等等。

也是一边研究一边实践,还观摩了不少b站视频,最终总算是撸出来了(虽然有点丑,而且里面的传感器模块固定的不是很牢固)。

pic11

软件

主板

本着能用就行的原则,做一个调包侠,Arduino比较易于上手,开发难度较低,刚好对ESP32平台的支持也相当不错,于是就在电脑上搭建了PlatformIO+Arduino的开发环境,开搞。

前面那些元器件,如果是输出数字信号的,Arduino里面基本上都有对应的现成的库可以直接使用,对于输出模拟信号的元件,基本也就是一个电压值,使用支持ADC的引脚来进行采样就行了(后面我才知道,ESP32自带的ADC精度似乎比较差,导致测量结果的精度不够,如果之后再做类似的东西,我应该会购买额外的ADC芯片来处理模拟信号)。

另外还增加了从网络自动获取当前时间的功能,程序会定期轮询wifi连接情况,如果连接成功,则会向NTP服务器请求同步时间,客户端收到当前时间后,跟开机时间做个差,存在内存中即可断开wifi。之后每次刷新屏幕的时候,就无需依赖网络了。

整体代码比较简单,就是在主循环loop里面固定间隔刷新各个传感器的数据,然后把获取到的数据拼接成对应的字符串,通过串口发送给串口屏即可

下面是部分代码,篇幅所限省略了部分逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 刷新DHT22的数据
#define DHT_PIN 21
#define DHT_TYPE DHT22
DHT_Unified dht(DHT_PIN, DHT_TYPE);
void refreshDHT()
{
// 有现成的库可以直接拿来用
sensors_event_t event;
dht.temperature().getEvent(&event);
if (!isnan(event.temperature))
{
status->temperture = event.temperature;
}
dht.humidity().getEvent(&event);
if (!isnan(event.relative_humidity))
{
status->humidity = event.relative_humidity;
}
}

// 刷新MQ135的数据
#define MQ135_PIN 35
void refreshMQ135()
{
// 从指定引脚读取电压采样值,根据文档,从0.3V~4V映射到0~100的污染指数
auto rawVolts = analogReadMilliVolts(MQ135_PIN);
auto aq = map(rawVolts, 300, 4000, 0, 100);
status->airQuality = aq;
}

// 刷新其他传感器...

void loop()
{
auto now = millis();
if (now - lastRefreshTime > REFRESH_INTERNAL)
{
refreshDHT();
refreshMQ135();
// ...
}
}

屏幕

串口屏的功能很简单,不需要自己写驱动,上电之后自动开启,屏幕自带主控芯片,我们通过串口给它发送特定格式的指令就可以控制屏幕的显示了,我们只需要画一个UI,把图切好丢进淘晶驰的配套软件里,插上线烧写进屏幕里面就可以使用了,就像这样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void refreshTcj()
{
// 发送给串口屏形如[t2.txt="26.5"]的指令,就可以更新屏幕上对应组件的显示了
vector<string> cmdStrings;
char buf[128];
sprintf(buf, "t2.txt=\"%.1f\"", status->temperture);
cmdStrings.push_back(buf);
sprintf(buf, "t3.txt=\"%.1f\"", status->humidity);
cmdStrings.push_back(buf);
sprintf(buf, "t6.txt=\"%.0f\"", status->airQuality);
cmdStrings.push_back(buf);
// ...

for (auto cmd : cmdStrings)
{
// Serial1连接到串口屏,用来和串口屏通信
Serial1.print(cmd.c_str());
// 发送后,还需要接着三个0xff,表示指令结束
Serial1.print("\xff\xff\xff");
// Serial连接到调试串口,用来打印调试信息
Serial.println(cmd.c_str());
}
Serial.println("--------------------");
}

调试

之前没想到调试安装花了好久。。印象中之前从来没有用过电烙铁,这次还特地买了个练习板来练习焊接贴片元件。不过还好,最终总算是让整个机器运转起来了。不过还是遗留了几个BUG:

  1. 上电之后ESP32没有工作,比如要手动按一下RST按钮才能开始运行,查阅资料猜测是因为芯片要求芯片3V3引脚上电之后至少50us再把EN引脚拉高,我估计是Reset电路那里的电容小了,换个大点的估计就行了。
  2. 光照传感器显示的数据不准,有可能是因为外壳有一定厚度,挡住了部分光线。不过如果不看单位的话,显示的数据确实是可以根据环境光的明暗实时变化的,也就不管它了。
  3. 运行时间超过int32之后,会溢出变成负数。。屏幕上的的时间日期会显示异常,只有重启后重新连接手机热点重新对时。
  4. 断开wifi后,屏幕上的wifi图标仍然显示。这个是个小问题,影响不大。

理论上软件的bug是可以通过预留的串口插针来重新烧录程序的,不过我也懒得改了,开始折腾下一个大项目咯~

截止本文截稿,如果不算短暂的重启,这台机器连续正常运行了差不多半年了,一切正常~