基于Arduino nano的北斗定位火灾报警系统设计
2022-12-22 # 实用教程

设计目标

以一个情景来说,当发生火灾时,本系统会检测到火灾,然后通过GSM通信模块,将定位和火警信息以短信形式发送给目标手机,并控制蜂鸣器实现报警。按下复位按键可以使系统重新工作在监测状态。(点此查看效果)

准备阶段

长文,有点啰嗦,但几乎涵盖所有可能会遇到的问题,望读者有所准备。不过内容简单,请放心阅读。

硬件

元件清单 简介
Arduino nano主控板,1块 作为系统主控制器
北斗定位模块,1个 本系统采用的是正点原子的ATK-1218-BD模块,带有源天线的版本,用来实现室内的定位
GSM通信模块,1个 本系统采用的是SIM900A模块,通过它发送报警短信,SIM800系列的好像也兼容
MQ-2烟雾传感器模块,1个 用来检测烟雾和可燃气体
火焰传感器模块,1个 通过红外线接收管来检测火焰
蜂鸣器模块,1个 用于报警
USB-TTL模块,1个 非必要,只是用于电脑调试GSM模块,最好是CP2102芯片那款
microUSB数据线,1根 用来供电以及给nano主控板烧录程序
面包板或洞洞板,1块 用于放置电路,面包板需若干杜邦线配合使用

如果跟我一样是用洞洞板搭电路的话,还需电烙铁、焊锡、电线等工具和材料。

软件

  1. Arduino IDE,开发Arduino必备的集成开发环境,官网下载地址 (点此跳转),根据自己操作系统版本下载对应软件安装包即可
  2. GNSS_Viewer,专门用来观察和设置北斗模块的软件,购买ATK-1218-BD模块时店家会提供资料,当然也可以到正点原子的资料下载中心获取,点此跳转
  3. TinyGPS++库,是由Mikal Hart开发的Arduino第三方库,用来解析北斗模块发来的定位信息。这是作者主页,里面有下载地址还有对这个库的详细介绍。我用的1.0.2版本的库,百度网盘地址,提取码: q5bn,如果不知道库怎么用那就下我这个老版本吧。库的安装比较容易这里就不赘述了。

设计阶段

管脚接线

下表展示了Arduino nano与各模块的连接关系。

Arduino nano引脚 作用 接法
D2 普通数字输出口 接蜂鸣器I/O引脚
D8 软串口1的RX 接北斗模块TX引脚
D9 软串口1的TX 接北斗模块RX引脚
D10 软串口2的RX 接GSM模块TX引脚
D11 软串口2的TX 接GSM模块RX引脚
A0 普通模拟输入口 接火焰传感器AO引脚
A1 普通模拟输入口 接烟雾传感器AO引脚

原理图

用AD画的,点开图片可以看得清晰点,内容跟上面其实差不多,就是看起来生动一点。

alt原理图

报警解除电路可以忽略,实际上它是通过Arduino nano内部的复位电路实现的,只不过现在把它画到外面了。蜂鸣器电路偷懒用了之前画的,不过不影响。

程序流程图

最底下的报警解除按键实际上是通过硬件电路实现的,就是nano本身自带的复位电路。把它画在程序流程图里只是为了说明蜂鸣器报警程序虽然是死循环但是可以通过复位解除。

alt程序流程图

调试阶段

注意事项

  1. GSM模块需要5V2A的电源供电,否则模块工作可能不正常;
  2. 单独调试GSM模块跟电脑串口通信时需要质量较好的USB转TTL模块,我用自己之前买的劣质CH340芯片的USB转TTL模块在调试时失败了,电脑串口能识别但是发AT指令给GSM模块没反应,换了CP21202芯片的USB转TTL模块就好了;
  3. 北斗定位模块的有源天线最好放到阳台外,这样才能搜到卫星;
  4. 虽然Arduino nano软串口支持的最高波特率是115200,但测试时发现,只有波特率工作在9600才正常,这也是为什么下面把北斗模块波特率也设成9600的原因;
  5. 北斗模块每次使用前需要用GNSS_Viewer软件对它的波特率修改为9600(这个可能是我的北斗模块有问题,没法把设置写进flash);
  6. 烟雾传感器使用前需要预热几分钟,具体多久看串口,打印的数值不咋跳了,就预热完了;
  7. 火焰传感器对光是敏感的,也就是说中午太阳光很亮的话,它也会触发,这也是为什么加烟雾传感器的原因。

火焰、烟雾传感器

这两模块均可通过以下代码测试好坏,检测到火焰或烟雾后,串口打印的数值会发生较大变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define flameSensor A0 //火焰传感器
#define smogSensor A1 //烟雾传感器
void setup()
{
pinMode(flameSensor,INPUT);
//pinMode(smogSensor,INPUT);
Serial.begin(9600); //初始化串口,便于调试
}

void loop()
{
//每秒打印传感器检测值(数值范围为0-1023)
Serial.println(analogRead(flameSensor));
//Serial.println(analogRead(flameSensor));
delay(1000);
}

一个参考:我的火焰传感器在晚上测出来数值在1015-1017徘徊,拿手机闪光灯照射数值会降到850以下;烟雾传感器正常值应该在500以下,检测到烟雾数值会超过500,当时测得应该67百这样。
以上仅是参考,实际上每个传感器检测的返回值各不相同,只要检测到火焰或烟雾时,数值上有较大的变动即可说明模块工作正常。

SIM900A通信模块

  1. 插入手机卡后,与USB转TTL模块(按TXD-RXD、RXD-TXD、GND-GND连接)好后,供上5V2A的电,不出意外的话,模块已经正常工作了。
  2. 可以给模块上的手机号打电话,正常拨通振铃的话说明模块应该没问题。然后就可以把USB转TTL模块接到电脑上,打开串口助手,设置通信参数(波特率9600,数据位8,停止位1),然后打开对应的串口,发送指令‘AT’,如果收到模块的回复‘OK’,那就说明电脑与模块串口通信正常。
  3. 然后就可以通过以下程序烧录到单片机,测试单片机与GSM模块的串口通信是否正常。
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
#include <SoftwareSerial.h>

SoftwareSerial SIM900A(10,11); //GSM模块串口
String msg="Hello!"; //短息的内容

void sendMeg()
{
SIM900A.println("AT");
delay(1000);
SIM900A.println(F"AT+CMG=1");
delay(1000);
SIM900A.println("AT+CMGS=\"13xxxxxxxx2\""); //填接收短信的号码
delay(1000);
SIM900A.print(msg);
SIM900A.print("\r\n");
delay(1000);
SIM900A.write(0x1A);
}

void setup()
{
SIM900A.begin(9600);
sendMeg();
}

void loop(){

}

如果程序中的手机号码收到了对应短信内容,说明模块工作正常,测试完成。

北斗模块

跟GSM一样与USB转TTL模块连接好后,可以通过GNSS_Viewer软件查看模块是否定位成功,我的北斗模块通信波特率默认是115200,所以定位成功后需设置波特率为9600。
然后就可以通过TinyGPS++自带的例程测试本模块,该例程在正常安装好TinyGPS++库后,应该能在Arduino IDE的菜单栏(File->Examples->TinyGPS++)里找到。

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
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

TinyGPSPlus gps;
SoftwareSerial ss(8,9);

float latitude;
float longitude;

void setup()
{
Serial.begin(9600); //set the baud rate of serial port to 9600;
ss.begin(9600); //set the GPS baud rate to 9600;
}

void loop()
{
ss.listen();
while (ss.available() > 0){
gps.encode(ss.read()); //The encode() method encodes the string in the encoding format specified by encoding.
if (gps.location.isUpdated()){
latitude = gps.location.lat(); //gps.location.lat() can export latitude
longitude = gps.location.lng();//gps.location.lng() can export latitude
Serial.print("Latitude=");
Serial.print(latitude, 6); //Stable after the fifth position
Serial.print(" Longitude=");
Serial.println(longitude, 6);
delay(500);
}
}
}

没问题的话,该例程会在串口打印输出经纬度信息,即说明北斗模块定位正常且该库正常解析出定位信息中的经纬度信息。

蜂鸣器模块

该模块可通过下面这个程序检测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define Buzz 2    //定义蜂鸣器引脚为D2

void BuzzOnOff(){
digitalWrite(Buzz,HIGH);
delay(100);
digitalWrite(Buzz,LOW);
delay(100);
}

void setup()
{
pinMode(Buzz,OUTPUT);
}

void loop()
{
BuzzOnOff();
}

它够吵就说明模块正常。

Arduino软串口

因为GSM模块与北斗模块都要用串口通信,而nano仅有一个物理串口,这就需要用软件实现串口,简称软串口。关于例程在Arduino IDE的菜单栏(File->Examples->SoftwareSerial->SoftwareSerialExample)里可以找到。
下面是我测试软串口的程序,它将nano的D8、D9、D10、D11这四个脚模拟成两个串口,一个串口发信息,另一个串口可以收到该信息。简而言之,让Arduino自言自语,哦,准确的说,好像是我在自言自语。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <SoftwareSerial.h>

SoftwareSerial beidou(8, 9); // RX, TX
SoftwareSerial gsm(10, 11);

void setup() {
beidou.begin(57600);
gsm.begin(4800);
beidou.println("Hi!This is beidou.");
gsm.println("Hello,It's gsm");
}

void loop() {
if (beidou.available()) {
gsm.write(beidou.read());
}
if (gsm.available()) {
beidou.write(gsm.read());
}
}

测试需要两个USB转TTL模块分别(按TXD-D8、RXD-D9、GND-GND和TXD-D10、RXD-D11、GND-GND)连接,然后打开两个串口助手软件才能看到效果。没问题的话,你在一个串口助手发消息,可以在另一个串口助手收到消息。

最终实现

下面是最终测试的视频

过会儿手机就会受到火灾报警短信,如下所示。

alt短信

总程序

就60几行代码?是的,就这么点。你完全可以读懂每一行代码在做什么。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <SoftwareSerial.h>
#include <TinyGPS++.h>
#define Buzz 2 //定义蜂鸣器引脚为D2
#define flameSensor A0 //火焰
#define smogSensor A1 //烟雾

TinyGPSPlus gps;
SoftwareSerial Beidou(8,9); //北斗串口RX, TX
float latitude;
float longitude;

SoftwareSerial SIM900A(10,11); //GSM模块串口
String msg="EMERGENCY! FIRE ALARM!!!\r\nPositioin Here: ";

void sendMeg()
{
SIM900A.println("AT");
delay(1000);
SIM900A.println("AT+CMGF=1");
delay(1000);
SIM900A.println("AT+CMGS=\"12345654321\""); //接收号码
delay(1000);
SIM900A.print(msg);
SIM900A.print("Latitude="); //发送定位信息
SIM900A.print(latitude, 6);
SIM900A.print(", Longitude=");
SIM900A.print(longitude, 6);
SIM900A.print("\r\n");
delay(1000);
SIM900A.write(0x1A);
}

void BuzzOnOff(){
digitalWrite(Buzz,HIGH);
delay(100);
digitalWrite(Buzz,LOW);
delay(100);
}

void setup()
{
pinMode(flameSensor,INPUT);
pinMode(smogSensor,INPUT);
pinMode(Buzz,OUTPUT);
SIM900A.begin(9600);
Beidou.begin(9600);
}

void loop()
{
Beidou.listen();
while(Beidou.available() > 0){
/* The encode() method encodes the string in the encoding format
specified by encoding. */
gps.encode(Beidou.read());
if (gps.location.isUpdated()){
latitude = gps.location.lat(); //gps.location.lat() can export latitude
longitude = gps.location.lng();//gps.location.lng() can export latitude
delay(500);
}
if(analogRead(smogSensor)>500 && analogRead(flameSensor)<650){
BuzzOnOff();
sendMeg();
while(1){BuzzOnOff();}
}
}
}