热度 6
2023-12-31 00:37
1789 次阅读|
2 个评论
项目背景:甲醛在生活也比较常见:新装修的公寓,不知名的家居,新装修的房子以及吸烟的密闭环境,你没有听错,吸烟过程有大量甲醛,甲醛严重超标。故需要DIY甲醛数显检测,检测时间短,反应快:进口甲醛传感器一致性很好,精度高,sensirion甲醛传感器SFA30,Arduino程序代码:Arduino UNO 驱动OLED实时显示甲醛传感器相关参数:甲醛浓度、温度、湿度,ws2812试试通过颜色确定当前环境中甲醛浓度等级 甲醛相关资料可参考抖音老爸评测中吸烟仓中甲醛浓度,或参考中国知网:吸烟烟雾中的甲醛、室内香烟、电子烟释放甲醛和VOCs的散发特征及健康风险分析等文章 sensirion甲醛传感器SFA30,甲醛传感器一般采用电化学原理,利用甲醛与催化剂作用产生微弱电压信号,经过运放处理后将微弱信号放大然后根据建立模型(湿度、温度补偿)得到甲醛浓度,本教程使用的是sensirion的SFA30甲醛传感器 甲醛传感器原理: 传感器模型框架图: 项目实现:基于Arduino平台搭建甲醛传感器实时显示、指示、读取数据的检测仪,真实检查环境中的甲醛浓度,专业甲醛检测仪动辄上万元,对于普通家庭很难承担,sensirion甲醛传感器SFA30基于电化学原理,通过半透膜筛选甲醛分子,真真切切检测环境中甲醛浓度。 项目原理图: PCB图: 3D渲染图: 实物图: 视频介绍: 代码附件: #include #include #define SENSOR_SERIAL_INTERFACE Serial SensirionUartSfa3x sfa3x; #include #ifdef __AVR__ #include #endif #define PIN 11 Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800); #include "font.h" int16_t hcho; int16_t relativeHumidity; int16_t temperature; int SFA30_date=0; uint16_t error; int scl=A5;//定义OLEDSCL为A1引脚 int sda=A4;//定义OLEDSCL为A0引脚 int res=10;//定义OLE DRES为10引脚(IIC通信可不用设置) #define OLED_SCLK_Clr() digitalWrite(scl,LOW)//SCL #define OLED_SCLK_Set() digitalWrite(scl,HIGH) #define OLED_SDIN_Clr() digitalWrite(sda,LOW)//SDA #define OLED_SDIN_Set() digitalWrite(sda,HIGH) #define OLED_RST_Clr() digitalWrite(res,LOW)//RES 注:此引脚是为了配合SPI驱动模块改成I2C驱动模块使用的(改装的话必须接),如果买的是I2C模块,请忽略此引脚。 #define OLED_RST_Set() digitalWrite(res,HIGH) #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据 uint8_t OLED_GRAM ;//将要显示的缓存内容 void setup() { pinMode(2, OUTPUT);pinMode(3, OUTPUT);//SFA30传感器供电引脚使能 digitalWrite(3, HIGH);digitalWrite(2, LOW);//SFA30传感器供电输出 pinMode(A3, OUTPUT);pinMode(A2, OUTPUT); //OLED供电引脚使能 digitalWrite(A3,LOW); digitalWrite(A2,HIGH);//OLED供电输出 OLED_Init();//OLED初始化 OLED_ColorTurn(0);//0正常显示 1反色显示 OLED_DisplayTurn(0);//0正常显示 1翻转180度显示 SENSOR_SERIAL_INTERFACE.begin(115200); while (!SENSOR_SERIAL_INTERFACE) {delay(100);} sfa3x.begin(SENSOR_SERIAL_INTERFACE); sfa3x.deviceReset();//SFA30传感器复位 sfa3x.startContinuousMeasurement();//SFA30传感器开始测试 //WS2812驱动 #if defined (__AVR_ATtiny85__) if (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // End of trinket special code strip.begin(); strip.setBrightness(50); strip.show(); // Initialize all pixels to 'off' //WS2812驱动 } void loop() { while(1) { sfa3x.readMeasuredValuesOutputFormat2(hcho, relativeHumidity,temperature);//获取SFA30传感器数据 //OLED_Clear(); OLED_ShowString(0,0,"T:",16);OLED_ShowNum(40,0,temperature/200,2,16); OLED_ShowString(56,0,".",16);OLED_ShowNum(64,0,temperature%200,2,16);OLED_DrawCircle(82,4,1);OLED_ShowString(86,0,"C",16); OLED_ShowString(0,16,"H:",16);OLED_ShowNum(40,16,relativeHumidity/100,2,16); OLED_ShowString(56,16,".",16);OLED_ShowNum(64,16,relativeHumidity%100,2,16); OLED_ShowString(82,16,"%",16); OLED_ShowString(0,32,"HCHO:",16);OLED_ShowNum(40,32,hcho/5,3,16);OLED_ShowString(66,32,"ppb",16); OLED_ShowString(0,48,"date:",16);OLED_ShowNum(40,48,SFA30_date,3,16); OLED_Refresh(); delay(50);SFA30_date++; if(hcho<307) {colorWipe(strip.Color(0, 255, 0), 50); }// Green else if(hcho<615) {colorWipe(strip.Color(0, 0, 255), 50); }// Green else if(hcho<615) {colorWipe(strip.Color(255, 0, 0), 50); }// Red } } //反显函数 void OLED_ColorTurn(uint8_t i) { if(!i) OLED_WR_Byte(0xA6,OLED_CMD);//正常显示 else OLED_WR_Byte(0xA7,OLED_CMD);//反色显示 } //屏幕旋转180度 void OLED_DisplayTurn(uint8_t i) { if(i==0) { OLED_WR_Byte(0xC8,OLED_CMD);//正常显示 OLED_WR_Byte(0xA1,OLED_CMD); } else { OLED_WR_Byte(0xC0,OLED_CMD);//反转显示 OLED_WR_Byte(0xA0,OLED_CMD); } } //起始信号 void I2C_Start(void) { OLED_SDIN_Set(); OLED_SCLK_Set(); OLED_SDIN_Clr(); OLED_SCLK_Clr(); } //结束信号 void I2C_Stop(void) { OLED_SCLK_Set(); OLED_SDIN_Clr(); OLED_SDIN_Set(); } //等待信号响应 void I2C_WaitAck(void) //测数据信号的电平 { OLED_SCLK_Set(); OLED_SCLK_Clr(); } //写入一个字节 void Send_Byte(uint8_t dat) { uint8_t i; for(i=0;i<8;i++) { OLED_SCLK_Clr();//将时钟信号设置为低电平 if(dat&0x80)//将dat的8位从最高位依次写入 { OLED_SDIN_Set(); } else { OLED_SDIN_Clr(); } OLED_SCLK_Set();//将时钟信号设置为高电平 OLED_SCLK_Clr();//将时钟信号设置为低电平 dat<<=1; } } //发送一个字节 //向SSD1306写入一个字节。 //mode:数据/命令标志 0,表示命令;1,表示数据; void OLED_WR_Byte(uint8_t dat,uint8_t mode) { I2C_Start(); Send_Byte(0x78); I2C_WaitAck(); if(mode){Send_Byte(0x40);} else{Send_Byte(0x00);} I2C_WaitAck(); Send_Byte(dat); I2C_WaitAck(); I2C_Stop(); } //更新显存到OLED void OLED_Refresh(void) { uint8_t i,n; for(i=0;i<8;i++) { OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址 OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址 OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址 for(n=0;n<128;n++) OLED_WR_Byte(OLED_GRAM ,OLED_DATA); } } //清屏函数 void OLED_Clear(void) { uint8_t i,n; for(i=0;i<8;i++) { for(n=0;n<128;n++) { OLED_GRAM =0;//清除所有数据 } } OLED_Refresh();//更新显示 } //画点 //x:0~127 //y:0~63 void OLED_DrawPoint(uint8_t x,uint8_t y) { uint8_t i,m,n; i=y/8; m=y%8; n=1<