基于nb-iot和arduino的气象站(二)

基于nb-iot和arduino的气象站(二)PM2.5和GPS传感器

上一篇介绍了温湿度传感器和紫外线传感器的使用。

这一篇介绍pm2.5和GPS传感器的使用。

一、PM2.5传感器

我使用的传感器为DSL-03。

DSL-03是一款激光式PM2.5传感器,内置激光器和光电接收组件,采用光散射原理,激光在颗粒物上产生散射光,由光电接收器件转变为电信号,再通过特定算法计算出PM2.5质量浓度、PM10质量浓度、PM0.3~PM2.5粒子个数、PM2.5~PM10粒子个数。

基于nb-iot和arduino的气象站(二)_第1张图片

1.1接线说明

  1. GND接GND
  2. VCC接5V
  3. TX接arduino RX
  4. RX接arduino TX

1.2数据格式

该传感器采用异步串行通信方式,以帧为单位进行通讯。帧格式固定,每一帧由9个字节组成,分命令帧和应答帧。

  1. 传感器上电后,需要给传感器发送开机指令
帧头 帧命令 帧内容 校验和 帧尾
0xAA 0x01 0x00000000 0x0166 0xBB

传感器向外部设备回应帧具体内容为

帧头 帧命令 帧内容 校验和 帧尾
0xAA 0x01 0x00004F4B 0x0200 0xBB

1. 读PM2.5和PM10的值

外部设备向传感器发送命令

帧头 帧命令 帧内容 校验和 帧尾
0xAA 0x02 0x00000000 0x0167 0xBB

传感器向外部设备回应帧具体内容为

帧头 帧命令 帧内容 校验和 帧尾
0xAA 0x02 0x01310123 0x01BD 0xBB

计算PM2.5质量浓度:(字节5)*256+(字节6)=0x01*256+0x23=291(ug/m3)

计算PM2.5质量浓度:(字节3)*256+(字节4)=0x01*256+0x31=305(ug/m3)

计算字节和校验:

(字节1)+(字节2)+(字节3)+(字节4)+(字节5)+(字节6)+(字节9)=(字节7)*256+(字节8)

1.3参考代码

#include 
#include 
SoftwareSerial mySerial(5, 6);
uint16_t pm25;
uint16_t pm10;

void setup() {
  mySerial.begin(9600);
  Serial.begin(9600);
  pmOn();
  delay(1000);
}
void pmRead(){
  uint8_t data[] = {0xAA,0x02,0x00,0x00,0x00,0x00,0x01,0x67,0xBB};
  mySerial.write(data,9);
  delay(100);
  for(int i=0;i<9;i++){
    if (mySerial.available()) {
      data[i] = mySerial.read();
    }
  }
  if(data[0]==0xAA && data[8]== 0xBB){
    pm25 = data[4]*256 + data[5];
    pm10 = data[2]*256 + data[3];

  }
}

void pmOn(){
  uint8_t data[] = {0xAA,0x01,0x00,0x00,0x00,0x00,0x01,0x66,0xBB};
  mySerial.write(data,9);
}

void pmOff(){
  uint8_t data[] = {0xAA,0x03,0x00,0x00,0x00,0x00,0x01,0x68,0xBB};
  mySerial.write(data,9);
}

在代码中,用到了软串口,因为arduino的硬串口只有1个,不够这么多传感器同时使用。

二、GPS传感器

我使用的传感器采用U-BLOX NEO-6M模组,体积小巧。模块增加放大电路,有利于无源陶瓷天线快速搜星。

基于nb-iot和arduino的气象站(二)_第2张图片

2.1接线说明

VCC接3.3V~5V

GND接GND

TX接arduino RX

RX接arduino TX

2.2数据格式

帧格式形如:$aaccc,ddd,ddd,…,ddd*hh(CR)(LF)

  1. “$”:帧命令起始位
  2. aaccc:地址域,前两位为识别符(aa),后三位为语句名(ccc)
  3. ddd…ddd:数据
  4. “*”:校验和前缀(也可以作为语句数据结束的标志)
  5. hh:校验和(check sum),$与*之间所有字符ASCII码的校验和(各字节做异或运算,得到校验和后,再转换16进制格式的ASCII字符)
  6. (CR)(LF):帧结束,回车和换行符
序号 命令 说明 最大帧长
1 $GPGGA GPS定位信息 72
2 $GPGSA 当前卫星信息 65
3 $GPGSV 可见卫星信息 210
4 $GPRMC 推荐定位信息 70
5 $GPVTG 地面速度信息 34
6 $GPGLL 大地坐标信息
7 $GPZDA 当前时间(UTC)信息

以GPRMC为例:

GPRMCRecommendedMinimumSpecificGPS/TransitData G P R M C ( 推 荐 定 位 信 息 , R e c o m m e n d e d M i n i m u m S p e c i f i c G P S / T r a n s i t D a t a ) GPRMC语句的基本格式如下:
$GPRMC,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)*hh(CR)(LF)
(1) UTC时间,hhmmss(时分秒)
(2) 定位状态,A=有效定位,V=无效定位
(3) 纬度ddmm.mmmmm(度分)
(4) 纬度半球N(北半球)或S(南半球)
(5) 经度dddmm.mmmmm(度分)
(6) 经度半球E(东经)或W(西经)
(7) 地面速率(000.0~999.9节)
(8) 地面航向(000.0~359.9度,以真北方为参考基准)
(9) UTC日期,ddmmyy(日月年)
(10)磁偏角(000.0~180.0度,前导位数不足则补0)
(11) 磁偏角方向,E(东)或W(西)
(12) 模式指示(A=自主定位,D=差分,E=估算,N=数据无效)
举例如下:

$GPRMC,023543.00,A,2308.28715,N,11322.09875,E,0.195,,240213,,,A*78

2.3参考代码

int L = 13; //LED指示灯引脚

struct
{
    char GPS_Buffer[80];
    bool isGetData;     //是否获取到GPS数据
    bool isParseData;   //是否解析完成
    char UTCTime[11];       //UTC时间
    char latitude[11];      //纬度
    char N_S[2];        //N/S
    char longitude[12];     //经度
    char E_W[2];        //E/W
    bool isUsefull;     //定位信息是否有效
} Save_Data;

const unsigned int gpsRxBufferLength = 600;
char gpsRxBuffer[gpsRxBufferLength];
unsigned int ii = 0;

void setup()    //初始化内容
{
    Serial.begin(9600);     
    Serial.println("ILoveMCU.taobao.com");
    Serial.println("Wating...");

    Save_Data.isGetData = false;
    Save_Data.isParseData = false;
    Save_Data.isUsefull = false;
}

void loop()     //主循环
{
    gpsRead();  //获取GPS数据
    parseGpsBuffer();//解析GPS数据
    printGpsBuffer();//输出解析后的数据
}

void errorLog(int num)
{
    Serial.print("ERROR");
    Serial.println(num);
    while (1)
    {
        digitalWrite(L, HIGH);
        delay(300);
        digitalWrite(L, LOW);
        delay(300);
    }
}

void printGpsBuffer()
{
    if (Save_Data.isParseData)
    {
        Save_Data.isParseData = false;

        Serial.print("Save_Data.UTCTime = ");
        Serial.println(Save_Data.UTCTime);

        if(Save_Data.isUsefull)
        {
            Save_Data.isUsefull = false;
            Serial.print("Save_Data.latitude = ");
            Serial.println(Save_Data.latitude);
            Serial.print("Save_Data.N_S = ");
            Serial.println(Save_Data.N_S);
            Serial.print("Save_Data.longitude = ");
            Serial.println(Save_Data.longitude);
            Serial.print("Save_Data.E_W = ");
            Serial.println(Save_Data.E_W);
        }
        else
        {
            Serial.println("GPS DATA is not usefull!");
        }       
    }
}

void parseGpsBuffer()
{
    char *subString;
    char *subStringNext;
    if (Save_Data.isGetData)
    {
        Save_Data.isGetData = false;
        Serial.println("**************");
        Serial.println(Save_Data.GPS_Buffer);

        for (int i = 0 ; i <= 6 ; i++)
        {
            if (i == 0)
            {
                if ((subString = strstr(Save_Data.GPS_Buffer, ",")) == NULL)
                    errorLog(1);    //解析错误
            }
            else
            {
                subString++;
                if ((subStringNext = strstr(subString, ",")) != NULL)
                {
                    char usefullBuffer[2]; 
                    switch(i)
                    {
                        case 1:memcpy(Save_Data.UTCTime, subString, subStringNext - subString);break;   //获取UTC时间
                        case 2:memcpy(usefullBuffer, subString, subStringNext - subString);break;   //获取UTC时间
                        case 3:memcpy(Save_Data.latitude, subString, subStringNext - subString);break;  //获取纬度信息
                        case 4:memcpy(Save_Data.N_S, subString, subStringNext - subString);break;   //获取N/S
                        case 5:memcpy(Save_Data.longitude, subString, subStringNext - subString);break; //获取纬度信息
                        case 6:memcpy(Save_Data.E_W, subString, subStringNext - subString);break;   //获取E/W

                        default:break;
                    }

                    subString = subStringNext;
                    Save_Data.isParseData = true;
                    if(usefullBuffer[0] == 'A')
                        Save_Data.isUsefull = true;
                    else if(usefullBuffer[0] == 'V')
                        Save_Data.isUsefull = false;
                }
                else
                {
                    errorLog(2);    //解析错误
                }
            }
        }
    }
}

void gpsRead() {
    while (Serial.available())
    {
        gpsRxBuffer[ii++] = Serial.read();
        if (ii == gpsRxBufferLength)clrGpsRxBuffer();
    }

    char* GPS_BufferHead;
    char* GPS_BufferTail;
    if ((GPS_BufferHead = strstr(gpsRxBuffer, "$GPRMC,")) != NULL || (GPS_BufferHead = strstr(gpsRxBuffer, "$GNRMC,")) != NULL )
    {
        if (((GPS_BufferTail = strstr(GPS_BufferHead, "\r\n")) != NULL) && (GPS_BufferTail > GPS_BufferHead))
        {
            memcpy(Save_Data.GPS_Buffer, GPS_BufferHead, GPS_BufferTail - GPS_BufferHead);
            Save_Data.isGetData = true;
            clrGpsRxBuffer();
        }
    }
}

void clrGpsRxBuffer(void)
{
    memset(gpsRxBuffer, 0, gpsRxBufferLength);      //清空
    ii = 0;
}

下一篇将介绍NB-IoT模块的使用。

你可能感兴趣的:(硬件)