(一)项目简介
一.项目名称:微定气体浓度检测仪
采用arduino单片机接收传感器信号,并驱动隔膜泵,实现定量吸取气体。
使用Android与arduino通信,Android显示接收数据,并对arduino进行控制。
二.项目开展
该项目从大一暑假开始,独立开展,历时一个月完成。
(二)检测仪之电路设计
一.硬件清单
1.单片机 arduino mega 2560
2.传感器 气体浓度传感器 DFRobot的MiCS-2714 H2测量范围为1-1000ppm
温湿度传感器 AHT10 湿度精度:0.01 温度精度:0.01
压强传感器 bmp180 测量范围:30-110kpa
3.隔膜泵 可调速直流无刷 nidec 微型真空泵
4.风扇 AVC调速小风扇
5.android 旧手机
二.APP软件
## 1.库
使用了github 开源 usb-serial-for-android 库
> 链接:https://github.com/mik3y/usb-serial-for-android
## 2.界面设计
## 3.代码实现
我将app代码放在了github上,可直接下载使用
注释详细
## 4.实现功能
使用该usb库,可直接连接单片机,连接串口,实现通信。
发送数据,直接使用函数write 即可 ,我因为个人需求 每次发送8个字节数据,一个字节存放两个16进制数据
TextView one = (TextView) findViewById(R.id.hex_num);
//将编辑框中的数据直接发出
String s = one.getText().toString();
if (!s.isEmpty()) {
byte[] a = HEX.hexStringToByteArray(s);
try {
port.write(a, 1000);
} catch (IOException e) {
e.printStackTrace();
}
}
接收数据,使用广播接听即可,我将接听到的数据一位一位放进了自定义的Application的字节队列中。
由于是新手,一开始调试的时候,read函数不能很好的一次性读取8位数据,所以我将8位数据分开发送。
经调试,只要在每发送的一位一位数据中间延迟1ms左右,就可以保证数据接收不会丢失。
(这个请根据自己的情况调整,因为我只需要传送几个传感器的数据,所以牺牲了速度,保证的数据)
//设置广播接听数据
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final SerialInputOutputManager.Listener mListener =
new SerialInputOutputManager.Listener() {
@Override
public void onRunError(Exception e) {
//Log.d(TAG, "Runner stopped.");
}
@Override
public void onNewData(final byte[] data) {
runOnUiThread(()->{
//因为我一次只发送了一位 所以 每次取一位即可
app.value.addLast(data[0]);
app.data_count++;
});
}
};
数据处理:我使用定时器Handle+timer的方式,循环处理放在队列中的数据
Timer
//创建一个定时器 循环 发送一个msg 值 启动handle
Timer timer=new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
Message msg=new Message();
//发送一下,触法handle
myhandle.sendMessage(msg);
}
};
//启动 设置循环时间
timer.schedule(task,0,5000);
Handle
//创建handle 用来处理数据
private final Handler myhandle =new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
while((app.data_count/8)>0)
{
//新建8位数组 for循环
//当到第6 7 8 时 若不是ff 则 退出for循环 (防错机制)
byte[] bytes=new byte[8];
int i=0;
for(;i<8;i++)
{
bytes[i]=app.value.getFirst();
if(i>=5){
if(bytes[i]!=(byte)0xff)
{
break;
}
}
app.value.poll();
app.data_count--;
}
if(i == 8){
data_handle.S_to_handle(HEX.toHexString(bytes),da_1,da_2,da_3,da_4,da_5,da_6);
}
}
return false;
}
});
界面设计
三.单片机
单片机代码看分为两个部分,一个是接收数据部分,一个是发送数据部分。
传感器的使用皆使用arduine的库,简单易操作。
定义
//气压
#include<Wire.h>
#include<Adafruit_BMP085.h>
Adafruit_BMP085 bmp;
uint32_t data_pa =0; //气压值 100 000 整数
//mems 气体浓度
#include<DFRobot_MICS.h>
#define CALIBRATION_TIME 3
#define ADC_PIN A0
#define POWER_PIN 26
DFRobot_MICS_ADC mics(ADC_PIN,POWER_PIN);
float data_gas=0; //浓度值 1-1000ppm 小数点一位
//泵
#define MOtor_beng 9
int PWM_beng=0; //占空比 0-255
//风扇
#define Motor_feng 8 //风扇
int PWM_feng=0; //占空比 0-255
//测速
#define FG_Pin 24 //测速引脚
#define asd 2 //每圈脉冲数
int16_t max_n=0; //上升最大值
int16_t max_m=0; //下降最大值
int16_t speed=0; //转速
//AHT 湿度 温度传感器
#include <Adafruit_AHT10.h>
Adafruit_AHT10 aht;
float data_temp=0; //温度值,小数点两位
float data_humi=0; //湿度值,小数点两位
#define a 10000 //sent 延迟
bool bmp_s=0;
bool aht_s=0;
bool mems_s=0;
bool mems_s_tobegin=0;
bool beng_s=0;
bool feng_s=0;
//存储int数
byte data_shu[4];
android发送启动信号,arduino判断并启动或停止
if(Serial.available()>=8)
{
byte byts[8];
Serial.readBytes(byts,8);
//第一位为 标识位
int c=byts[0];
switch (c)
{
case 10:
case 11:
//如果全为00 则不再读取 温度 湿度
if(byts[1]==0&&byts[2]==0&&byts[3]==0&&byts[4]==0)
{
aht_s=0;
}
else
{
aht.begin();
aht_s=1;
}
break;
case 12:
//如果全为0 则不再读取 压强
if(byts[1]==0&&byts[2]==0&&byts[3]==0&&byts[4]==0)
{
bmp_s=0;
}
else
{
bmp.begin();
bmp_s=1;
}
break;
case 13:
//打开mems
if(!mems_s)
{
mics.begin();
mems_s=1;
}
//如果为1 启动mems
if(byts[1]==1||byts[2]==1||byts[3]==1||byts[4]==1)
{
uint8_t mode = mics.getPowerState();
if(mode == SLEEP_MODE){
mics.wakeUpMode();
mems_s_tobegin=1;
}
}
else
{
mics.sleepMode();
mems_s_tobegin=0;
}
break;
case 14:
if(byts[4]!=00)
{
if(!feng_s)
{
feng_s=1;
}
//设置PWM
PWM_feng=byte_to_int(byts[1],byts[2],byts[3],byts[4]);
analogWrite(Motor_feng,PWM_feng);
}
else
{
feng_s=0;
}
break;
case 15:
//如果全为00 停止
if(byts[4]!=00)
{
//启动
if(!beng_s)
{
beng_s=1;
}
///设置PWM
PWM_beng=byte_to_int(byts[1],byts[2],byts[3],byts[4]);
analogWrite(MOtor_beng,255-PWM_beng);
}
else
{
beng_s=0;
}
break;
default:
break;
}
}
数据发送都是重复的,就不一一列出了
//压强发送
if(bmp_s)
{
read_pa();
int_to_bytes(data_shu,data_pa);
Serial.write(0x0c);
delayMicroseconds(a);
Seriasadite(data_shu[0]);
delayMicroseconds(a);
Serial.write(data_shu[1]);
delayMicroseconds(a);
Serial.write(data_shu[2]);
delayMicroseconds(a);
Serial.write(data_shu[3]);
delayMicroseconds(a);
Serial.write(0xff);
delayMicroseconds(a);
Serial.write(0xff);
delayMicroseconds(a);
Serial.write(0xff);
delayMicroseconds(a);
delay(1000);
}
以下是我定义的一些函数,都很简单。
//读取压强
void read_pa()
{
data_pa=bmp.readPressure();
}
//读取气体浓度
void read_H2()
{
data_gas =mics.getGasData(H2);
}
//读取湿度 温度
void read_Shi_temp()
{
sensors_event_t humidity, temp;
aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
data_temp=temp.temperature;
data_humi=humidity.relative_humidity;
}
//将16字节数组转为 int
uint32_t byte_to_int(byte one,byte two,byte three,byte four)
{
return (one&0xff)<<24
|(two&0xff)<<16
|(three&0xff)<<8
|(four&0xff);
}
//将int 转为字节数组
void int_to_bytes(byte array[4],uint32_t i)
{
array[0] = (byte) (i>>24);
array[1] = (byte) ((i & 0xFF0000)>>16);
array[2] = (byte) ((i &0xFF00)>>8);
array[3] = (byte) ((i&0xFF));
}
四.电路连接
1.各传感器与单片机直接连接即可,需要用到相同的引脚(如两个传感器都要连接SDA,SDL),直接并联即可
不同传感器的地址不同,不同的库函数也不同,可独立使用,互不影响。
2.泵与风扇要与12V电源相连,同时需与单片机共地。
(三)检测仪之气路设计
一. 原理
由泵运动,形成负压,使气体被吸入气室,得以检测。
二.气路设计
1.流量计:采用了玻璃转子流量计 2.5m-25ml/min
2.节流阀:控制压强,流速。
3.净化装置:除去空气中的灰尘,水分,防止泵的堵塞。
4.缓冲罐:由于泵本身的特性,气体呈波动性变化,加一个缓冲罐,可以平稳气流。
5.泵
6.单向阀:防止气体回流
7.气室:采样亚克力板制作
8.电磁阀:控制气室气压,放出气体。
(四)检测仪之最终效果
实验所得:
在泵的PWM调速时,90的占空比,能使气体的流速稳定在17ml左右
这个是整体效果: