甲醛检测仪开源项目-产品级开发(一)

简介: 甲醛检测仪开源项目-产品级开发(一)

前阵子开源了一个基于TencentOS tiny物联网操作系统的危险气体探测仪项目,这次,我们再来开源一个新的项目-甲醛检测仪,但是做项目之前,有必要了解下接下来要做的一些模块以及如何来进行集成。

1、简介

WZ-S型甲醛检测模组是英国达特公司开发的,是用于将环境中甲醛的含量转换成浓度值,标准化数字输出,便于系统集成。

640.png

2、特点

640.png

3、典型应用场景

640.png

4、硬件引脚及技术指标

640.png

5、传感器通讯协议

该传感器采用的是串行通讯方式,也就是我们常用的串口,串口配置参数如下:


  • 波特率:9600
  • 数据位:8位
  • 停止位:1位
  • 校验位:无


传感器在出厂后默认为主动上报,每隔1s上报一次浓度值,命令行格式如下:

640.png

一般情况下我们直接拿来用即可。

6、软件编程(以STM32为例)

以下开发板为TOS_EVB_GO开发板,也就是前阵子TencentOS公众号发表的一篇文章的那个,链接如下:


基于TencentOS Tiny接入腾讯连连微信小程序,打造您自己的智能家居产品


TOS_EVB_G0 开发板是由腾讯TencentOS-tiny团队设计的一款物联网开发板,板载资源如下:


  • 主控芯片采用STM32G070RB,Flash空间仅有128KB、RAM空间仅有20KB;
  • 板载腾讯云定制固件版ESP8266 WIFI模组;
  • 板载E53传感器标准接口,方便连接各种E53传感器;
  • 板载0.91'OLED显示屏幕;
  • 板载8MB SPI Flash,可用于固件升级;
  • 板载CH340 转串口连接,可以使用一根USB线连接至电脑,查看串口日志;

640.png

基于该开发板编写的达特传感器驱动例程位于:

https://gitee.com/morixinguan/bear-pi.git

640.png

以上拓展模块是腾讯基于E53接口设计的一个传感器模块,所以小熊派也是支持的,如下:

640.png

由于在小熊派上使用比较顺手,所以现在我已经对它爱不释手了,无论是工作做实验还是平时练习,以下配置、编程基于小熊派开发板。

6.1、STM32CubeMX关于传感器的配置

640.png


640.png

640.png

640.png

配置DMA接收,个人习惯DMA+空闲中断的方式。

6.2、其它配置
6.2.1 时钟

640.png

640.png

6.2.2 SWD调试口

640.png

6.2.3 调试串口

640.png

6.2.4 SPI OLED配置

640.png

其余的部分直接复用之前文章的一些接口即可,然后生成工程:

640.png

640.png

6.3 程序编写

在程序编写之前先来了解一些基本的概念,有助于我们后面产品的实现。


(1)ppm、ppb、ppt是什么?


表达溶液的浓度时,1ppm=1ug/mL;表达固体中成分含量时,1ppm即为1ug/g或1g/t。


所以1ppb=1ppm的千分之一,ppm即百万分之一,ppb即1亿分之一,ppt即千亿分之一。


所以ppm是10的-6次方,ppb是10的-9次方,ppt是10的-12次方


(2)浓度及浓度单位换算


1ppm = 1000ppb


1ppb  = 1000ppt


ppm 即:mg/L(毫克/升)


ppm 即:mg/L(毫克/升)


ppm 即:mg/L(毫克/升)

6.3.1 达特传感器通讯协议解析

由于达特甲醛传感器出厂时固定是发9个字节,所以我们可以直接用下面这个结构体来表示:

/*甲醛传感器协议*/
typedef struct
{
 /*起始位*/
 uint8_t start_bit ;
 /*气体名称*/
 uint8_t gas_name  ;
 /*单位*/
 uint8_t unit ;
 /*小数位数*/
 uint8_t decimal_places ;
 /*气体浓度高位*/
 uint8_t gas_density_high ;
 /*气体浓度低位*/
 uint8_t gas_density_low ;
 /*满量程高位*/
 uint8_t full_range_high ;
 /*满量程低位*/
 uint8_t full_range_low ;
 /*校验值*/
 uint8_t checksum_value ;
}Dart_Sensor_Procol_TypeDef ;

针对以上结构体我们很容易根据官方手册说明写出如下解析函数:

Dart_Sensor_Procol_TypeDef Dart_Sensor_Data_Parse(uint8_t *Data)
{
 uint16_t temp = 0 ;
 uint16_t check_sum = 0 ;
 uint16_t check_sum_negate = 0 ;
 Dart_Sensor_Procol_TypeDef dart_sensor ;
  /*将接收到的协议数据直接转到结构体里进行存储*/
 memcpy(&dart_sensor,Data,sizeof(Dart_Sensor_Procol_TypeDef));
  /*计算校验值*/
 check_sum = dart_sensor.gas_name + dart_sensor.unit +      \
 dart_sensor.gas_density_low + dart_sensor.gas_density_high +   \
 dart_sensor.full_range_high + dart_sensor.full_range_low + dart_sensor.decimal_places ;
 check_sum_negate = ~check_sum ;
 temp = check_sum_negate + 1 ;
 if((temp & 0xff) != dart_sensor.checksum_value) 
 {
  memset(&dart_sensor,0,sizeof(Dart_Sensor_Procol_TypeDef));
  return dart_sensor ;
 }
 return dart_sensor ;
}

关于这个Data是怎么直接转结构体的,可以参考我的一位朋友邓工最近发表的一篇文章,里面图文并茂的说明了这种骚操作,文章链接如下,点击即可跳转:


【进阶】"结构体嵌入共联体"在协议解析中的神操作!


在用户层次,用户不需要关心协议是怎么解析的,所以我们只需要给用户提供一个获取数据的结构体和函数即可,然后通过头文件dart_sensor.h提供给用户,而协议解析部分直接放在dart_sensor.c文件里就可以了,如下:

#ifndef __DART_SENSOR_H
#define __DART_SENSOR_H
#include <stdint.h>
#include <string.h>
/*
1ppm = 1000ppb
1ppb = 1000ppt
ppm = mg/L(毫克/升)
ppb = ug/L(微克/升)
ppt = ng/L(纳克/升)
*/
typedef struct
{
 /*气体浓度*/
 float gas_density ; //ppm
 /*满量程*/
 float full_range ;  
}Dart_Sensor ;
Dart_Sensor Get_Dart_Sensor_Density(uint8_t *Data);
#endif //__DART_SENSOR_H

获取浓度Get_Dart_Sensor_Density函数的实现:


我看过的大多数浓度单位标识都是ppm,也就是xxx/mg/L的这种表示方法,所以这个接口就设计成下面这样。

/*
 获取气体浓度
 Data:  传感器数据
 return: xxx ppm
*/
Dart_Sensor Get_Dart_Sensor_Density(uint8_t *Data)
{
  Dart_Sensor sensor ;
  Dart_Sensor_Procol_TypeDef dart_sensor ;
  dart_sensor = Dart_Sensor_Data_Parse(Data);
  /*计算浓度,单位为ppm*/
  sensor.gas_density = ((dart_sensor.gas_density_high << 8) + (dart_sensor.gas_density_low))/1000.0 ;
  /*当前传感器量程*/
  sensor.full_range  = ((dart_sensor.full_range_high << 8)  + (dart_sensor.full_range_low))/1000.0 ; 
  return sensor ;
}

6.3.2 达特传感器通讯库封装

既然用户不需要关心过程,那我们可以给这个简单的解析过程做一个lib,这样就相当于一个模块,提供.h和.lib即可,接下来建立一个STM32L431的工程,然后将.c和.h放在一个文件夹内,通过Keil包含进来

640.png

640.png

640.png

然后在Output下选择创建库,接下来点击编译即可生成:

640.png

注意,这里建立的这个库仅在该环境下适用。思考一下,如何做到平台通用呢?

6.3.3 案例编写

(1)开启串口空闲中断

/*开启空闲中断*/
__HAL_UART_ENABLE_IT(uartHandle, UART_IT_IDLE);
//开启DMA接收
memset(sensor_handler.SensorU3Buffer, 0, SENSOR_U3_BUFFER_SIZE);
HAL_UART_Receive_DMA(&huart3, (uint8_t*)sensor_handler.SensorU3Buffer, SENSOR_U3_BUFFER_SIZE);

(2)串口空闲中断处理 串口接收数据结构:

//固定9个字节
#define SENSOR_U3_BUFFER_SIZE       9
typedef struct
{
  /*表示接收到了*/
  uint8_t  BufferReady;
  /*数据缓存区*/
  uint8_t  SensorU3Buffer[SENSOR_U3_BUFFER_SIZE];
}Sensor_HandleTypeDef;
extern Sensor_HandleTypeDef sensor_handler ;

以下是数据采集过程,非常简单:

/**
  * @brief This function handles USART3 global interrupt.
  */
void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */
 if(RESET != __HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE))
  {
      __HAL_UART_CLEAR_IDLEFLAG(&huart3);
   HAL_UART_DMAStop(&huart3);
   sensor_handler.BufferReady = 1 ;
  }
  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */
  /* USER CODE END USART3_IRQn 1 */
}

当接收到空闲中断时,代表数据已经接收到了,此时sensor_handler.BufferReady置1,代表数据已经接收完成。


(3)数据解析处理与应用逻辑 在while循环中编写如下代码:

while (1)
{
    /*接收到一帧数据*/
    if(1 == sensor_handler.BufferReady)
    {
        /*接收标志位清0*/
        sensor_handler.BufferReady = 0 ;
        /*判断包头数据是否正确*/
        if(sensor_handler.SensorU3Buffer[0] == 0xFF && sensor_handler.SensorU3Buffer[1] == 0x17)
        {
            //调用解析函数
            sensor = Get_Dart_Sensor_Density(sensor_handler.SensorU3Buffer);
            /*业务逻辑开始*/
            sprintf(display_buf, "%.3fmg/L", sensor.gas_density);
            LCD_ShowCharStr(70, 100, 170, display_buf, BLACK, WHITE, 24);
            /*业务逻辑结束*/
            //重新打开DMA继续接收新的一帧数据
            memset(sensor_handler.SensorU3Buffer, 0, SENSOR_U3_BUFFER_SIZE);
            HAL_UART_Receive_DMA(&huart3, (uint8_t*)sensor_handler.SensorU3Buffer, SENSOR_U3_BUFFER_SIZE);
        }
    }
}

640.png

如何判断接收到的这帧数据到底对不对呢?我们只需要根据协议手册判断前两个字节是否为0xff和0x17即可。

7、运行结果

640.png

8、指标衡量

模块咱们用起来了,如何判断来衡量甲醛含量的技术指标呢?下面这张图截的是淘宝上某个商家对检测标准的说明:

640.png

9、思考与练习

前面我开源了一个基于TencentOS tiny的危险气体探测仪项目,是否能在那个项目上稍微改改,变成一个新的产品级项目,让一个新项目:甲醛探测仪迅速开发出来呢?

640.png

淘宝上其实已经有很多优秀的产品案例,如下所示,界面做得相当漂亮了:

640.png

640.png

640.png

是否能做出一个跟以上界面类似的开源项目呢?

如下图所示,小熊派开源生态社区工作小组的阿正大佬已经做出来了一个类似的作品,给他点赞!

640.png

同时也希望更多热爱开源的小伙伴加入我们的小熊派开源生态社区工作小组,该工作小组为高质量社区,不同于一般群,只玩技术不闲聊,不接受潜水大佬,所以人不在多而在于精;无论小伙伴们玩的是什么平台(不局限于小熊派),只要是热爱开源,有创意有想法,乐于持续分享,且目前在码云/Github等社区有作品的玩家即可(私聊我的微信,拉你入群)

640.png

本节代码已同步到码云的代码仓库中,获取方法如下:

1、新建一个文件夹

640.png

2、使用git clone远程获取小熊派例程存放的代码仓库

项目开源仓库:

https://gitee.com/morixinguan/bear-pi.git

640.png

640.png

我还将之前做的一些项目以及练习例程在近期内全部上传完毕,与大家一起分享交流:

640.png

往期精彩

C语言将xxx.bin文件转为数组


开源STM32产品:无线点菜宝使用评测


C语言表驱动法编程实践(精华帖,建议收藏并实践)


分享一个在Keil开发环境中配置代码格式化工具Astyle(美化代码风格)

目录
相关文章
|
10月前
|
安全 网络安全 数据安全/隐私保护
利用Docker的网络安全功能来保护容器化应用
通过综合运用这些 Docker 网络安全功能和策略,可以有效地保护容器化应用,降低安全风险,确保应用在安全的环境中运行。同时,随着安全威胁的不断变化,还需要持续关注和研究新的网络安全技术和方法,不断完善和强化网络安全保护措施,以适应日益复杂的安全挑战。
279 61
|
存储 SQL 分布式计算
Parquet与ORC高性能列式存储
Parquet与ORC高性能列式存储
941 0
Parquet与ORC高性能列式存储
|
9月前
|
存储 SQL 人工智能
Lindorm:AI和具身智能时代的海量多模数据服务
本次分享由阿里云资深技术专家沈春辉介绍Lindorm数据库在AI和具身智能时代的应用。Lindorm定位于提供海量多模数据服务,融合了结构化、半结构化及非结构化数据的处理能力,支持时序、地理位置、文本、向量等多种数据类型。其核心特点包括多模一体化、云原生分布式架构、异步攒批写入、冷热数据分离、深度压缩优化、丰富索引和Serverless计算等,旨在提升研发效率并降低成本。Lindorm已广泛应用于车联网领域,覆盖60%国内头部车企,支撑近百PB数据规模,带来90%业务成本下降。
|
8月前
|
机器学习/深度学习 人工智能 自然语言处理
人工智能在虚拟客服中的关键作用:提升交互体验与服务效率
人工智能在虚拟客服中的关键作用:提升交互体验与服务效率
490 90
|
8月前
|
人工智能 自然语言处理 安全
《人工智能与鸿蒙Next携手,开启智慧教育创新之旅》
在数字化时代,人工智能与鸿蒙Next系统的融合为智慧教育带来创新机遇。通过智能学习路径规划、自适应内容推荐、自动作业批改、课堂互动增强等功能,打造个性化学习体验。鸿蒙Next的分布式技术和安全特性确保多设备无缝切换和数据隐私保护。同时,智能资源管理和优质资源共享平台优化教育资源配置,推动教育公平。这一结合为智慧教育注入新活力,助力全面升级。
291 30
|
8月前
|
机器学习/深度学习 数据采集 人工智能
设计文档:智能化医疗设备数据分析与预测维护系统
本系统的目标是构建一个基于人工智能的智能化医疗设备的数据分析及预测维护平台,实现对医疗设备运行数据的实时监控、高效处理和分析,提前发现潜在问题并进行预防性维修,从而降低故障率,提升医疗服务质量。
|
SQL 关系型数据库 MySQL
sqlite3自动插入创建时间和更新时间
在本文中,作者分享了如何使用sqlite3数据库来记录结构化日志,并实现主键ID自增、插入数据时自动填充创建时间(created_at)以及更新数据时更新时间(updated_at)的功能。首先,创建数据库和表`position_info`,然后通过修改表结构使ID字段为自动递增。接着,设置`created_at`和`updated_at`字段默认值为当前时间。最后,创建一个触发器在数据更新时自动更新`updated_at`。完整SQL代码包括表创建和触发器定义。
425 0
Python实现PowerPoint演示文稿到图片的批量转换
PowerPoint演示文稿作为展示创意、分享知识和表达观点的重要工具,被广泛应用于教育、商务汇报及个人项目展示等领域。然而,面对不同的分享场景与接收者需求,有时需要我们将PPT内容以图片形式保存与传播。 这样能够避免软件兼容性的限制,确保信息接收者无需安装特定软件即可查看内容,还便于在网络社交平台、博客、电子邮件中快速分享与嵌入。而用Python代码可以高效地实现PowerPoint演示文稿到图片的批量转换,从而提升工作效率。 本文将介绍如何使用Python实现PowerPoint演示文稿到图片的转换。
|
SQL 分布式计算 Hadoop
Hive on Tez 的安装配置
Hive on Tez 的安装配置
856 0
Hive on Tez 的安装配置
|
Linux 测试技术 数据安全/隐私保护
CentOS安装MeterSphere并实现无公网IP远程访问本地测试平台
CentOS安装MeterSphere并实现无公网IP远程访问本地测试平台