hi55234 发表于 2014-3-31 17:28:14

尝试DIY一个空气净化器 (7) 尝试为控制器加上GPS授时

测试程序1:


功能:小内存提取GPS中的2行数据
优点:理论可行,小于350字节一次性发送无误,实际上GPS测试也无误
缺点:严格测试(650字节)一次性发送,GPRMC收不到,估计是延迟上有问题,且没有做校验

char temp1,GPGGA,GPRMC;

String test1= "";

int jsq1,jsq2,jsq3;
boolean change0,change1,change2,change3;


void setup() {
   Serial.begin(9600);
}


void loop() {

jsq1=0;
jsq2=0;
jsq3=0;

change0=0;
change1=0;
change2=0;
change3=0;

while (Serial.available() > 0) {//监听串口输入

change0=1;
       
if(!change1){
       
if(jsq1<4){
temp1=Serial.read();
delayMicroseconds(1200);
jsq1++; //jsq1最终值为4,temp1:0-4共计5个字节

if(jsq1==4){//值得注意的是,此时,temp1并没有赋值
temp1=Serial.read(); //为temp1赋值
delayMicroseconds(1200);
panduan1();//此时,收集到5个字符,可以进行判断
}


}else{

//传递过程,temp1只有5个字节
temp1=temp1;
temp1=temp1;
temp1=temp1;
temp1=temp1;
temp1=Serial.read();

delayMicroseconds(1200);

panduan1();

}

///////////////////////////////////////////////////////////////////////////
}else if(change2){

//GPGGA
GPGGA=Serial.read();
delayMicroseconds(1200);

if(GPGGA=='*'){ //GPGGA字符串的结束
change1=0;//恢复以前的状态,继续由临时数组temp1判断
change2=0;//不再执行这段代码
jsq1=0;//计数器1还原

}else jsq1++;

//////////////////////////////////////////////////////////////////////////
}else if(change3){
//GPRMC

GPRMC=Serial.read();
delayMicroseconds(1200);

if(GPRMC=='*'){
change1=0;//恢复以前的状态,继续由临时数组temp1判断
change2=0;//不再执行这段代码
jsq1=0;//计数器1还原

}else jsq1++;

}



}









//最后的收尾工作
if(change0){

Serial.print("GPGGA=");
Serial.println(GPGGA);
Serial.print("GPRMC=");
Serial.println(GPRMC);
Serial.println("----------------------");

//置0
for(int col=0;col<80;col++)GPGGA=0;
for(int col=0;col<80;col++)GPRMC=0;
}

}


void panduan1(){

//清空字符串,且数组转字符串
//神奇的地球,数组==“字符串”貌似不能成立
test1 = "";
test1 += temp1;
test1 += temp1;
test1 += temp1;
test1 += temp1;
test1 += temp1;




if(test1=="GPGGA"){//字符串GPGGA

   Serial.println("find GPGGA");
   change1=1;
   change2=1;
   change3=0;

   //交接工作,接下来的数据都会给GPGGA
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
   jsq1=5;
   
}else if(test1=="GPRMC"){//字符串GPRMC

   Serial.println("find GPRMC");
   change1=1;
   change2=0;
   change3=1;

GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;   
   jsq1=5;
}

}

zoologist 发表于 2014-3-31 19:35:39

提个问题:空气净化器应该在屋子里使用吧?gps 能工作正常么?

hi55234 发表于 2014-3-31 21:52:54

zoologist 发表于 2014-3-31 19:35 static/image/common/back.gif
提个问题:空气净化器应该在屋子里使用吧?gps 能工作正常么?

空气净化器的控制模块与净化器是可以分离的,以无线进行连接,所以,控制器本身可以充当空气质量检测器~~

PS:考窗边的话,GPS可以定位~~~获得时间就更不在话下

hi55234 发表于 2014-3-31 21:54:05

程序2,加入异或校验:

char temp1,GPGGA,GPRMC;

String test1= "";


int jsq1,jsq2,jsq3;
boolean change1,change2,change3;

int GPGGAlong,GPRMClong;//GPGGA、GPRMC的长度
int yihuoyunsuan;
boolean jiaoyanjieguoA,jiaoyanjieguoB;


void setup() {
   Serial.begin(9600);
}


void loop() {

jsq1=0;
jsq2=0;
jsq3=0;


change1=0;
change2=0;
change3=0;

GPGGAlong=0;
GPRMClong=0;
jiaoyanjieguoA=0;
jiaoyanjieguoB=0;

while (Serial.available() > 0) {//监听串口输入


if(!change1){

if(jsq1<4){
temp1=Serial.read();
delayMicroseconds(1200);
jsq1++; //jsq1最终值为4,temp1:0-4共计5个字节

if(jsq1==4){//值得注意的是,此时,temp1并没有赋值
temp1=Serial.read(); //为temp1赋值
delayMicroseconds(1200);
panduan1();//此时,收集到5个字符,可以进行判断
}


}else{

//传递过程,temp1只有5个字节
temp1=temp1;
temp1=temp1;
temp1=temp1;
temp1=temp1;
temp1=Serial.read();

delayMicroseconds(1200);

panduan1();

}

///////////////////////////////////////////////////////////////////////////
}else if(change2){

//GPGGA
GPGGA=Serial.read();
delayMicroseconds(1200);

if(GPGGA=='*'){ //GPGGA字符串的结束
change1=0;//恢复以前的状态,继续由临时数组temp1判断
change2=0;//不再执行这段代码
GPGGAlong=jsq1-2;//记录这段字符串的长度GPGGAlong=jsq1-3+1(0位有一个字节)
jsq1=0;//计数器1还原

}else jsq1++;

//////////////////////////////////////////////////////////////////////////
}else if(change3){
//GPRMC

GPRMC=Serial.read();
delayMicroseconds(1200);

if(GPRMC=='*'){
change1=0;//恢复以前的状态,继续由临时数组temp1判断
change2=0;//不再执行这段代码
GPRMClong=jsq1-2;
jsq1=0;//计数器1还原

}else jsq1++;

}



}




/////////////////////////////////////////////////////异或运算,验证其数据可信

if(GPGGAlong>0){ //字符串GPGGA的验证

for(int col=0;col<GPGGAlong;col++){
if(col==0)yihuoyunsuan=GPGGA;
else yihuoyunsuan=yihuoyunsuan ^ GPGGA;
}

panduan2();


if(test1==GPGGA && test1==GPGGA){
//一致,则说明数据是有效的,输出校验结果
jiaoyanjieguoA=1;
}else{
//不一致
jiaoyanjieguoA=0;
}


}


if(GPRMClong>0){//字符串GPRMC的验证

for(int col=0;col<GPRMClong;col++){
if(col==0)yihuoyunsuan=GPRMC;
else yihuoyunsuan=yihuoyunsuan ^ GPRMC;
}

panduan2();


if(test1==GPRMC && test1==GPRMC){
//一致,则说明数据是有效的,输出校验结果
jiaoyanjieguoB=1;
}else{
//不一致
jiaoyanjieguoB=0;
}


}


/////////////////////////////////////////////////

if(jiaoyanjieguoA && jiaoyanjieguoB){

Serial.print("GPGGA=");
Serial.println(GPGGA);
Serial.print("GPRMC=");
Serial.println(GPRMC);
Serial.println("----------------------");

}




//最后的收尾工作
if(GPRMClong>0 || GPGGAlong>0){







//置0
for(int col=0;col<80;col++)GPGGA=0;
for(int col=0;col<80;col++)GPRMC=0;
}

}


void panduan1(){ //找到特定字符串的开始

//清空字符串,且数组转字符串
//神奇的地球,数组==“字符串”貌似不能成立
test1 = "";
test1 += temp1;
test1 += temp1;
test1 += temp1;
test1 += temp1;
test1 += temp1;




if(test1=="GPGGA"){//字符串GPGGA

   Serial.println("find GPGGA");
   change1=1;
   change2=1;
   change3=0;

   //交接工作,接下来的数据都会给GPGGA
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
   jsq1=5;

}else if(test1=="GPRMC"){//字符串GPRMC

   Serial.println("find GPRMC");
   change1=1;
   change2=0;
   change3=1;

GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;   
   jsq1=5;
}

}


void panduan2(){ //异或运算的结果转化为字符串,以便于比较



if(yihuoyunsuan==0){
//校验数10进制为0,直接填充字符串00
test1="00";

}else if(yihuoyunsuan>15){
//此时转换为16进制以后,天然有2位,很理想,不用处理
test1 = String(yihuoyunsuan,HEX);

}else{
//校验数10进制为1-15时,转换为16进制就只有1位数,所以需要在前面填0
test1 = "0";
test1 += String(yihuoyunsuan,HEX);

}

test1.toUpperCase();//将其字母转换为大写


}

zoologist 发表于 2014-3-31 22:01:24

hi55234 发表于 2014-3-31 21:52 static/image/common/back.gif
空气净化器的控制模块与净化器是可以分离的,以无线进行连接,所以,控制器本身可以充当空气质量检测器~~ ...

有道理,不错啊

hi55234 发表于 2014-4-1 23:01:12


GPStest:14/4/1 22:55:12

altitude= 255

weixingshu= 5

latitude= latitude:xxxx.xxxx N

longitude= longitude:xxxxx.xxxx E

仅适用于经纬度小数点后4位的情况,如果是5位的,则要做相应的修改
至于通用的又要复杂一点,要判断逗号的位置,类似于提取年月日的过程(部分数据长度不确定),所以,这里以节约资源的名义偷工减料了


char temp1,GPGGA,GPRMC;

String test1= "";

String latitude = ""; //纬度
String longitude = ""; //经度


int jsq1,jsq2,jsq3;
boolean change1,change2,change3;

int GPGGAlong,GPRMClong;//GPGGA、GPRMC的长度
int yihuoyunsuan;
boolean jiaoyanjieguoA,jiaoyanjieguoB;


//UTC +8(北京时间)时、分、秒、年、月、日
int utc8s,utc8f,utc8m,utc8n,utc8y,utc8r;
//小月的数组
int xiaoyue={4,6,9,11};
//日进位,月进位,年进位,大小月判断值
boolean rijinwei,yuejinwei,nianjinwei,xiaoyueok;

boolean dingweiok;//定位ok

int weixingshu;//有效卫星数
int altitude; //海拔


void setup() {
   Serial.begin(9600);
}


void loop() {

jsq1=0;
jsq2=0;
jsq3=0;


change1=0;
change2=0;
change3=0;

GPGGAlong=0;
GPRMClong=0;
jiaoyanjieguoA=0;
jiaoyanjieguoB=0;


   //日进位,月进位
rijinwei=0;
yuejinwei=0;
nianjinwei=0;
xiaoyueok=0;

dingweiok=0;//默认未定位

weixingshu=0;//有效卫星数
altitude=0; //海拔

while (Serial.available() > 0) {//监听串口输入


if(!change1){

if(jsq1<4){
temp1=Serial.read();
delayMicroseconds(1200);
jsq1++; //jsq1最终值为4,temp1:0-4共计5个字节

if(jsq1==4){//值得注意的是,此时,temp1并没有赋值
temp1=Serial.read(); //为temp1赋值
delayMicroseconds(1200);
panduan1();//此时,收集到5个字符,可以进行判断
}


}else{

//传递过程,temp1只有5个字节
temp1=temp1;
temp1=temp1;
temp1=temp1;
temp1=temp1;
temp1=Serial.read();

delayMicroseconds(1200);

panduan1();

}

///////////////////////////////////////////////////////////////////////////
}else if(change2){

//GPGGA
GPGGA=Serial.read();
delayMicroseconds(1200);

if(GPGGA=='*'){ //GPGGA字符串的结束
change1=0;//恢复以前的状态,继续由临时数组temp1判断
change2=0;//不再执行这段代码
GPGGAlong=jsq1-2;//记录这段字符串的长度GPGGAlong=jsq1-3+1(0位有一个字节)
jsq1=0;//计数器1还原

}else jsq1++;

//////////////////////////////////////////////////////////////////////////
}else if(change3){
//GPRMC

GPRMC=Serial.read();
delayMicroseconds(1200);

if(GPRMC=='*'){
change1=0;//恢复以前的状态,继续由临时数组temp1判断
change2=0;//不再执行这段代码
GPRMClong=jsq1-2;
jsq1=0;//计数器1还原

}else jsq1++;

}



}




/////////////////////////////////////////////////////异或运算,验证其数据可信

if(GPGGAlong>0){ //字符串GPGGA的验证

for(int col=0;col<GPGGAlong;col++){
if(col==0)yihuoyunsuan=GPGGA;
else yihuoyunsuan=yihuoyunsuan ^ GPGGA;
}

panduan2();


if(test1==GPGGA && test1==GPGGA){
//一致,则说明数据是有效的,输出校验结果
jiaoyanjieguoA=1;
}else{
//不一致
jiaoyanjieguoA=0;
}


}


if(GPRMClong>0){//字符串GPRMC的验证

for(int col=0;col<GPRMClong;col++){
if(col==0)yihuoyunsuan=GPRMC;
else yihuoyunsuan=yihuoyunsuan ^ GPRMC;
}

panduan2();


if(test1==GPRMC && test1==GPRMC){
//一致,则说明数据是有效的,输出校验结果
jiaoyanjieguoB=1;
}else{
//不一致
jiaoyanjieguoB=0;
}


}


/////////////////////////////////////////////////

if(jiaoyanjieguoA && jiaoyanjieguoB){ //要求GPGGA、GPRMC同时通过奇偶验证才能进行下一步


gpstime(); //gps时间提取,日月年、时分秒、转为北京时间
Serial.println(test1);//这个字符串是gpstime

jingduweidu();//拾取经度、维度

if(dingweiok)haiba();//在定位有效的情况下拾取海拔、有效卫星数




Serial.print("altitude= ");
Serial.println(altitude);

Serial.print("weixingshu= ");
Serial.println(weixingshu);


Serial.print("latitude= ");
Serial.println(latitude);

Serial.print("longitude= ");
Serial.println(longitude);

Serial.print("GPGGA=");
Serial.println(GPGGA);
Serial.print("GPRMC=");
Serial.println(GPRMC);
Serial.println("----------------------");

}




//最后的收尾工作
if(GPRMClong>0 || GPGGAlong>0){
//置0
for(int col=0;col<80;col++)GPGGA=0;
for(int col=0;col<80;col++)GPRMC=0;
}

}


void panduan1(){ //找到特定字符串的开始

//清空字符串,且数组转字符串
//神奇的地球,数组==“字符串”貌似不能成立

test1 = String(temp1);
test1 += temp1;
test1 += temp1;
test1 += temp1;
test1 += temp1;




if(test1=="GPGGA"){//字符串GPGGA

// Serial.println("find GPGGA");
   change1=1;
   change2=1;
   change3=0;

   //交接工作,接下来的数据都会给GPGGA
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
GPGGA=temp1;
   jsq1=5;

}else if(test1=="GPRMC"){//字符串GPRMC

//Serial.println("find GPRMC");
   change1=1;
   change2=0;
   change3=1;

GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;
GPRMC=temp1;   
   jsq1=5;
}

}


void panduan2(){ //异或运算的结果转化为字符串,以便于比较



if(yihuoyunsuan==0){
//校验数10进制为0,直接填充字符串00
test1="00";

}else if(yihuoyunsuan>15){
//此时转换为16进制以后,天然有2位,很理想,不用处理
test1 = String(yihuoyunsuan,HEX);

}else{
//校验数10进制为1-15时,转换为16进制就只有1位数,所以需要在前面填0
test1 = "0";
test1 += String(yihuoyunsuan,HEX);

}

test1.toUpperCase();//将其字母转换为大写


}

void gpstime(){


//时间部分:UTC转为UTC+8,北京时间(时:分:秒)
//时:
test1 =String(GPRMC);//复用test1这个数组来节约内存
test1 +=GPRMC;
utc8s=test1.toInt()+8;
if(utc8s>23)rijinwei=1;//提示后面程序换算为北京时间的时候,日要+1天
utc8s=utc8s%24;//字符串转换为整数,参与运算

//分:
test1 =String(GPRMC);//复用test1这个数组来节约内存
test1 +=GPRMC;
utc8f=test1.toInt();//字符串转换为整数,参与运算

//秒:
test1 =String(GPRMC);//复用test1这个数组来节约内存
test1 +=GPRMC;
utc8m=test1.toInt();//字符串转换为整数,参与运算

////////////////////////////////////////////////
//提取UTC日期(日、月、年),这个日期位于GPRMC第9个数据中,所以定位第九个逗号的位置即可


jsq2=0;
jsq3=0;
for(int col=0;col<80;col++){
if(GPRMC==',')jsq2++;
if(jsq2==9){
jsq3=col;//找到9第个逗号的前一个位置,循环0开始,0的时候就已经后推了一位置
col=80;//找到9个逗号就跳出循环
}
}

//日
test1 =String(GPRMC);//复用test1这个数组来节约内存,9第个逗号前一位后推2个位置
test1 +=GPRMC;
utc8r=test1.toInt();//字符串转换为整数,参与运算

//月
test1 =String(GPRMC);//复用test1这个数组来节约内存
test1 +=GPRMC;
utc8y=test1.toInt();//字符串转换为整数,参与运算

//年
test1 =String(GPRMC);//复用test1这个数组来节约内存
test1 +=GPRMC;
utc8n=test1.toInt();//字符串转换为整数,参与运算


jsq2=0; //防止以后会用,先行清零
jsq3=0;

//讨论日、月、年进位(大月、小月、平年、闰年的问题)
if(rijinwei){

//先讨论2月的问题
if(utc8y==2 && utc8r==28){
if(utc8n%4==0)utc8r=29;//闰年可加一天
else {
utc8r=1;
yuejinwei=1;
}

}else{

//判断大小月
for(int col=0;col<4;col++){
if(xiaoyue==utc8y)xiaoyueok=1;
}

if(xiaoyueok && utc8r==30){ //小月最后一天
utc8r=1;
yuejinwei=1;
}else if(!xiaoyueok && utc8r==31){ //大月最后一天
utc8r=1;
yuejinwei=1;
}else{
utc8r++;//剩下的情况自加1就可以了
}
}

}

if(yuejinwei && utc8y==12){ //最后一月
utc8y=1;
nianjinwei=1;
}else if(yuejinwei){
utc8y++;
}

if(nianjinwei)utc8n++;

////////////////
test1 = "GPStest:";
test1 += utc8n;
test1 += "/";
test1 += utc8y;
test1 += "/";
test1 += utc8r;
test1 += " ";
test1 += utc8s;
test1 += ":";
test1 += utc8f;
test1 += ":";
test1 += utc8m;


}


void jingduweidu(){

if(GPRMC=='A'){//已定位
dingweiok=1;//已定位

latitude = "latitude:"; //纬度
for(int col=19;col<28;col++) latitude += GPRMC;
latitude += " ";
latitude += GPRMC;

longitude = "longitude:"; //经度
for(int col=31;col<41;col++)longitude += GPRMC;
longitude += " ";
longitude += GPRMC;

}

}


void haiba(){

//有效卫星数

test1 =String(GPGGA);
test1 +=GPGGA;
weixingshu=test1.toInt();//字符串转换为整数,参与运算

//海拔
//找到开始的逗号,与结束的逗号位置
for(int col=47;col<64;col++){
if(GPGGA==',' && jsq2==0)jsq2=col;//海拔开始的逗号
if(col>jsq2){
if(GPGGA==','){
jsq3=col;//海拔结束的逗号
col=64;//跳出循环
}
}
}

//拾取海拔高度
for(int col=jsq2+1;col<jsq3;col++){
if(col==jsq2+1)test1 =String(GPGGA);
else test1 +=GPGGA;
}

altitude=test1.toInt();//字符串转换为整数,参与运算;
}






hi55234 发表于 2014-4-19 17:44:52












可以在蓝牙gps、空气检测模块之间切换
页: [1]
查看完整版本: 尝试DIY一个空气净化器 (7) 尝试为控制器加上GPS授时