|
楼主 |
发表于 2016-1-9 09:15:42
|
显示全部楼层
融合浇花程序的草稿版
桌泰的GPS要是能设置只输出GPRMC,也可以来测试一下,这个,改几个参数就能用了
//输出控制:
//调试信息
#define tiaoshixinxi
#define tiaoshixinxi2
#include<EEPROM.h>
int EEPROMvalue; //EEPROM的值
char EEPROMtemp[25];
/*
设置格式如下
hhmmss,ddmmyy,A,B,C,D,E
A=数字1(0-127) 为由存储时间开始,间隔多少个单位时间浇水一次
B=数字2(0-127) 每次浇水多少个单位体积
C=数字3(0-127) 1单位时间=x*10min
D=数字4(0-127) 1单位体积=x*1L
D=数字5(0-127) 索引,如果分每条分配25字节,那么撑死能记录39条浇水记录
(浇水记录)程序存储格式如下
hhmmss,ddmmyy,A,B
A=数字1(48-49) 48未完毕 49已完毕
B=数字2(0-127) 已经浇水的单位体积
*/
#include <SoftwareSerial.h>
SoftwareSerial gps(8, 9); // RX, TX
char tempx[15];//提取GPS的时间到tempx数组中 hhmmss,ddmmyy
char GPRMC[100];//临时数组1,放gps数据的
unsigned long gpstimes;//gps计时器,如果超过一定时间10ms,则证明gps数据已经发送完毕
unsigned long quanjujishu;//全局计数,计算1秒之内,循环了多少次,这个次数在一定程度上表示了剩余性能
boolean konghangpanduan=1;//空行判断,因为在读取串口数据后并没有延迟,完全依赖全局循环,那么,特定程序是否执行,就全靠布尔量来标记了
int jsq1=0;
String jianyan="";//GPS数据包的ASCII异或校验
int yihuoyunsuan;//异或运算——校验结果
boolean jiaoyanjieguo=0;//校验结果
boolean dingweiok=0;//定位有效
boolean ruanchuankouchongqi=0;//重启软串口1次,无效时 冷启动GPS
int feiyuqileijiA=0;//非预期累积A,对应gps输出不是GGA 或 RMC时,累积到30次,定义GPS输出1次
int feiyuqileijiB=0;//非预期累积B,对应异或验证不能通过的情况,累积到30次,重启软串口1次,无效时 冷启动GPS 1次
int feiyuqileijiC=0;//非预期累积C,对应RMC长时间不能定位的情况,累积到10次,通过检查年数10位是否为默认值,来确定时间是否可用
boolean TimeOK=0;
int jilusuoyin=0;//记录索引
long shijijiangeshijian=0;//实际间隔时间
long yushejiangeshijian=0;//预设间隔时间
int daijiaoguanliang=0;//待浇灌量
boolean jiaohuakaishi=0;//浇花开始
///////////////////////////////////////////////////////
//浇花的一些定义
///////////////////////////////////////////////////////
int liuliangji=10;
int diancifa=11;
unsigned long meimiaoyilunhui;//每秒一轮回
boolean chongfupanduanA=0;//重复判定A
int jsqLLx=0;
float shishiliuliang=0;//实时流量
float leijiliuliang=0;//累计流量
unsigned long guzhangjianceA;//故障检测
int guzhangjiancejsq;//故障检测计数器
//////////////////////////////////////////////////////////////////////
void setup() {
gps.begin(19200);
Serial.begin(19200);
//EEPROM预处理
/*
设置格式如下
hhmmss,ddmmyy,A,B,C,D,E
000000,010116,60,20,12,10,0
A=数字1(0-127) 为由存储时间开始,间隔多少个单位时间浇水一次,默认设置为60(60*120min=5天)
B=数字2(0-127) 每次浇水多少个单位体积,默认数值为20,(20*10L=200L),对应ASCII的字符是 DC4 (device control 4) 设备控制4
C=数字3(0-127) 1时间单位=x*10min,默认数值为12
D=数字4(0-127) 1单位体积=x*1L 默认数值是10,对应ASCII的字符是 LF (NL line feed, new line) 换行键
E=数字5(0-127) 索引,如果分每条分配25字节,那么撑死能记录39条浇水记录,默认为字符0,ascII=48
(浇水记录)程序存储格式如下
hhmmss,ddmmyy,A,B
A=数字1(48-49) 48未完毕 49已完毕
B=数字2(0-127) 已经浇水的单位体积
*/
for(int col=0;col<24;col++) EEPROMtemp[col] = EEPROM.read(col);//读取预设参数
//打印预设参数
Serial.println();
Serial.println();
for(int col=0;col<14;col++)Serial.print(EEPROMtemp[col]);
int x=EEPROMtemp[14];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[16];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[18];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[20];
Serial.print(x);
Serial.print(",");
Serial.println(EEPROMtemp[22]);
jilusuoyin=EEPROMtemp[22]-48;//把索引数字化
if(jilusuoyin<40 && jilusuoyin>0){
//每条记录分配25字节空间,jilusuoyin为最近记录的首地址
for(int col=0;col<13;col++) EEPROMtemp[col] = EEPROM.read(col+jilusuoyin*25);//更新至最近一次的浇水时间
for(int col=0;col<13;col++)Serial.print(EEPROMtemp[col]);
//打印记录
Serial.write(EEPROM.read(13+jilusuoyin*25)); //直接打出数字对应的ASCII码
Serial.write(EEPROM.read(14+jilusuoyin*25));
Serial.write(EEPROM.read(15+jilusuoyin*25));
x=EEPROM.read(16+jilusuoyin*25);
Serial.println(x);
Serial.println("setup end");
}
}
// the loop routine runs over and over again forever:
void loop() {
if(jiaohuakaishi){
//具体浇花程序
jiaohua();
}else{
while (gps.available() > 0 && !TimeOK) {
gpstimes=millis();//重置gpstimes为millis()
konghangpanduan=0;//因为串口有数据,所以空行判断为0,即不空行
GPRMC[jsq1] = gps.read();//软串口读取GPS数据,并保存在临时数组GPRMC中
if(jsq1<99)jsq1++;//确保GPS跑飞时,或设置不当时,单片机自身数组不溢出
delayMicroseconds(500);
}
if(millis()-gpstimes>20 && !konghangpanduan){
konghangpanduan=1;
gpsyihuojianyan();//异或运算,校验GPS输出字符串的准确性
tiqushijian();//提取时间,装入tempx, 存储格式如下 hhmmss,ddmmyy
}
//在取得时间的情况下,讨论是否启动浇花程序
if(TimeOK){
TimeOK=0;
qidongjiaohuadepanduan();
}
}
}
void gpsyihuojianyan()
{
//异或运算,校验GPS输出字符串的准确性
//然而最终的jsq1 与实际的GPRMC的关系究竟如何,还是以实际调试为准
//手中这个jsq1就比GPRMC实际长度大2,估摸着换行符占了一位,但打印不显示,而程序在
//GPS串口传输完毕后,还对jsq1加了1,$ *XX不参与异或运算,第一位$ 所在数组为0
/*
Serial.print("jsq1= ");
Serial.println(jsq1);
Serial.println(GPRMC);
*/
for(int col=1;col<jsq1-5;col++){
if(col==1)yihuoyunsuan=GPRMC[col];
else yihuoyunsuan=yihuoyunsuan ^ GPRMC[col];
}
//因为定义int的时候,yihuoyunsuan的结果是以10进制DEC表示的,所以需转换为16进制HEX
//因为GPS校验数据0位也进行了传输,所以在转换的时候有必要将情况分类讨论
//(yihuoyunsuan=0)/(0<yihuoyunsuan<17)/(17<yihuoyunsuan)
if(yihuoyunsuan==0){
//校验数10进制为0,直接填充字符串00
jianyan="00";
}else if(yihuoyunsuan>15){
//此时转换为16进制以后,天然有2位,很理想,不用处理
jianyan = String(yihuoyunsuan,HEX);
}else{
//校验数10进制为1-15时,转换为16进制就只有1位数,所以需要在前面填0
jianyan = "0";
jianyan += String(yihuoyunsuan,HEX);
}
//实践中发现,jianyan中的字符是以小写方式存在,虽然Serial.println(yihuoyunsuan,HEX)会以大写方式打印出来
//但直接Serial.println(jianyan)就是小写的,这是啥情况?
//将其字母转换为大写,否则与GPS传输的校验码对比时大小写问题校验失败(GPS传输的校验码为大写的)
jianyan.toUpperCase();
///////////////////////////////////////////显示异或校验码,方便给GPS编程,虽然还没成功过
/*
Serial.print("jianyan= ");
Serial.println(jianyan);
Serial.print("jsq1= ");
Serial.println(jsq1);
Serial.println(GPRMC);
*/
if(jianyan[0]==GPRMC[jsq1-4] && jianyan[1]==GPRMC[jsq1-3] ){
//一致,则说明数据是有效的,输出校验结果
jiaoyanjieguo=1;
feiyuqileijiB=0;
ruanchuankouchongqi=0;//优先通过重启软串口解决校验不过的情况,不行能解决再冷启动GPS
}else{
//不一致
jiaoyanjieguo=0;
feiyuqileijiB++;
}
//对校验数组进行清零
jianyan="";
/*---------------------------异或运算,校验GPS输出字符串的准确性 end--------------------------*/
}
void tiqushijian()
{
//提取时间,装入tempx, 存储格式如下 hhmmss,ddmmyy
//并通过GPS命令操作,在信号不好时RMC、GGA的切换,GPS的重启
if(jiaoyanjieguo){ //通过异或校验的情况下,检查定位情况
jiaoyanjieguo=0;
#ifdef tiaoshixinxi
Serial.println(GPRMC);
#endif
//检查语句 RMC
if(GPRMC[4]=='M' && GPRMC[5]=='C'){
feiyuqileijiA=0;
//RMC 已定位标志判断
//$GPRMC,044344.858,V,0000.0000,N,00000.0000,E,,,030116,,*19
//$GPRMC,044342.000,A,0000.0000,N,00000.0000,E,0.00,,030116,,*1D
if(GPRMC[18]=='A'){
feiyuqileijiC=0;
dingweiok=1;
}else feiyuqileijiC++; //对应RMC长时间不能定位的情况
//对应RMC长时间不能定位的情况,900秒不定位,冷启动gps
if(feiyuqileijiC>900){
feiyuqileijiC=0;
gps.println("$PSRF101,0,0,0,000,0,0,12,4*10");//GPS COLD START
}
if(dingweiok || feiyuqileijiC>10){ //当GPS已定位,或虽未定位,但已经经过10秒
dingweiok=0;
//提取GPS的时间到tempx数组中 hhmmss,ddmmyy
//$GPRMC,044342.000,A,0000.0000,N,00000.0000,E,0.00,,030116,,*1D
for(int col=0;col<7;col++)tempx[col]=GPRMC[col+7];//时间位置固定,可直接提取
//日期稍微麻烦点,计算标志位","这个符号的位置,来规避长度变换(实际航速、实际航迹向这2个不确定因素)
int col2=0;
for(int col=0;col<jsq1-3;col++){
if(GPRMC[col]==',')col2++;
if(col2==9){
/*
Serial.print("GPRMC[col+5]=");
Serial.print(GPRMC[col+5]);
Serial.print(" GPRMC[col+6]= ");
Serial.println(GPRMC[col+6]);
//当gps信号弱,不能定位,通过检查gps 给出的年月日,是否是默认的160406 ddmmyy ,来判断时间的可用性,即年十位不为0
GPRMC[col+5] 是年的10位,GPRMC[col+6] 是年的个位,默认是06年,现在是16年,只要十位不是0(ascII大于48),那么就算时间可信
*/
if(GPRMC[col+5]>48){
feiyuqileijiC=0;
TimeOK=1;//时间可用标志
gps.end();//提取时间后关闭软串口
for(int col3=0;col3<7;col3++)tempx[6+col3]=GPRMC[col+col3];//提取时间
col=jsq1-3;//跳出循环
}
}
}
/*
//Serial.print(" TimeOK ");
Serial.print("tempx=");
Serial.println(tempx);
Serial.print("tempx[6]=");
Serial.println(tempx[6]);
if(tempx[6]==44)Serial.print("tempx[6]=44 ");
if(tempx[6]+2>44)Serial.print("tempx[6]+2>44 ");
int col=tempx[6];
Serial.print("col=");
Serial.println(col);
*/
}
}else feiyuqileijiA++;//对应gps输出不是 RMC时
}
if(feiyuqileijiA>30){ //对应gps输出不是 RMC时,累积到30次,定义GPS输出1次
feiyuqileijiA=0;
gps.println("$PSRF109,NMEA19200,NULL38400,GGA0,GLL0,GSA0,GSV0,RMC1,VTG0,USER0*32");
}
if(feiyuqileijiB>30){ //对应异或验证不能通过的情况,累积到30次,重启软串口1次,无效时 冷启动GPS 1次
feiyuqileijiB=0;
if(!ruanchuankouchongqi){ //优先重启软串口,ruanchuankouchongqi默认为0
ruanchuankouchongqi=!ruanchuankouchongqi;
gps.end();
gps.begin(19200);
}else{
ruanchuankouchongqi=!ruanchuankouchongqi;
gps.println("$PSRF101,0,0,0,000,0,0,12,4*10");//GPS COLD START
}
}
/*
Serial.print("feiyuqileijiA=");
Serial.print(feiyuqileijiA);
Serial.print(" feiyuqileijiB= ");
Serial.print(feiyuqileijiB);
Serial.print(" feiyuqileijiC= ");
Serial.print(feiyuqileijiC);
Serial.println(GPRMC);
*/
//收尾工作,清空GPS数组,jsq1归零
for(int col=0;col<100;col++)GPRMC[col]=0;
jsq1=0;
}
void qidongjiaohuadepanduan()//启动浇花的判断
{
/*
EEPROM设置格式如下
hhmmss,ddmmyy, A, B,C, D,E
000000,010116,60,20,12,10,0
A=数字1(0-127) 为由存储时间开始,间隔多少个单位时间浇水一次,默认设置为60(60*120min=5天)
B=数字2(0-127) 每次浇水多少个单位体积,默认数值为20,(20*10L=200L),对应ASCII的字符是 DC4 (device control 4) 设备控制4
C=数字3(0-127) 1时间单位=x*10min,默认数值为12
D=数字4(0-127) 1单位体积=x*1L 默认数值是10,对应ASCII的字符是 LF (NL line feed, new line) 换行键
E=数字5(0-127) 索引,如果分每条分配25字节,那么撑死能记录39条浇水记录,默认为字符0,ascII=48
(浇水记录)程序存储格式如下
hhmmss,ddmmyy,A,B
A=数字1(48-49) 48未完毕 49已完毕
B=数字2(0-127) 已经浇水的单位体积
*/
//进行浇花有2种情况, A、上次浇花因中断,现在继续 B、间隔时间到了,应该浇花
//情况A
/*对记录位进行初始化
hhmmss,ddmmyy,A,B
A=数字1(48-49) 48未完毕 49已完毕
B=数字2(0-127) 已经浇水的单位体积
*/
if(jilusuoyin > 0){//上次未完成的情况(索引大于0才可能有存在)
//上次未完成,浇花开始
if(EEPROM.read(14+jilusuoyin*25)==48){
jiaohuakaishi=1;
//浇花开始前的一些准备工作
guzhangjianceA=millis();//清掉故障检测与millis间的时间差距,否则一开始就是故障状态
meimiaoyilunhui=millis();//每一秒还原一次
}
}else if(millis()-quanjujishu>3000){ //讨论因间隔时间到了的浇花,情况,另外的间隔是定义的多久打开串口读取gps 数据一次,为方便调试,这里取3秒
//情况B,一个全新的开始
//时间到了的情况,EEPROMtemp[14]*EEPROMtemp[18] 个10min就是间隔,最大127*127,约2688小时,最小10min
quanjujishu=millis();
gps.begin(19200);
//shijijiangeshijian=计算上次浇水时间到现在,经理过多少个10min
long niancha=(tempx[11]-EEPROMtemp[11])*10*365*24*6+(tempx[12]-EEPROMtemp[12])*365*24*6;
long yuecha=(tempx[9]-EEPROMtemp[9])*10*30*24*6+(tempx[10]-EEPROMtemp[10])*30*24*6;
long richa=(tempx[7]-EEPROMtemp[7])*10*24*6+(tempx[8]-EEPROMtemp[8])*24*6;
long shicha=(tempx[0]-EEPROMtemp[0])*10*6+(tempx[1]-EEPROMtemp[1])*6;
shijijiangeshijian=niancha+yuecha+richa+shicha+tempx[2]-EEPROMtemp[2];
//预设间隔时间 = 间隔多少个单位时间 X 每个单位代表的时间(X * 10min)
yushejiangeshijian=EEPROMtemp[14]*EEPROMtemp[18];
if(shijijiangeshijian > yushejiangeshijian){
if(jilusuoyin<39)jilusuoyin++;
else jilusuoyin=1;
EEPROM.write(22, jilusuoyin+48);//更新EEPROM中的索引数值=原值+1,等于39的情况归0
for(int col=0;col<13;col++) EEPROMtemp[col] = tempx[col];//更新预设数组中的时间
for(int col=0;col<13;col++) EEPROM.write(col+jilusuoyin*25, tempx[col]);//更新EEPROM中的时间
/*对记录位进行初始化
hhmmss,ddmmyy,A,B
A=数字1(48-49) 48未完毕 49已完毕
B=数字2(0-127) 已经浇水的单位体积
*/
EEPROM.write(13+jilusuoyin*25, ',');//,
EEPROM.write(14+jilusuoyin*25, 48);//A
EEPROM.write(15+jilusuoyin*25, ',');//,
EEPROM.write(16+jilusuoyin*25, 0);//B
//浇花开始
jiaohuakaishi=1;
//浇花开始前的一些准备工作
guzhangjianceA=millis();//清掉故障检测与millis间的时间差距,否则一开始就是故障状态
meimiaoyilunhui=millis();//每一秒还原一次
}
#ifdef tiaoshixinxi
Serial.print("Detection value=");
Serial.println(shijijiangeshijian);
Serial.print(" set valuet=");
Serial.println(yushejiangeshijian);
Serial.println(tempx); //now time
for(int col=0;col<14;col++)Serial.print(EEPROMtemp[col]);//set time
int x=EEPROMtemp[14];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[16];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[18];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[20];
Serial.print(x);
Serial.print(",");
Serial.print(EEPROMtemp[22]);
Serial.print(" V=");
Serial.print(EEPROMtemp[16]*EEPROMtemp[20]);
Serial.print(" L Time =");
Serial.print(EEPROMtemp[14]*EEPROMtemp[18]/6/24);
Serial.println(" Day");
#endif
}
}
void jiaohua()//具体浇花程序
{
/*对记录位进行初始化
hhmmss,ddmmyy,A,B
A=数字1(48-49) 48未完毕 49已完毕
B=数字2(0-127) 已经浇水的单位体积
*/
//读取已浇灌体积,与预设浇灌体积相减,计算待浇灌量
int yijiaoguanliang=EEPROM.read(16+jilusuoyin*25);
daijiaoguanliang=EEPROMtemp[16]-yijiaoguanliang;
//累积流量达到一定值后,对EEPROM中的已浇灌量进行更新
if(leijiliuliang>10){
leijiliuliang=leijiliuliang-10;
yijiaoguanliang++;
EEPROM.write(16+jilusuoyin*25, yijiaoguanliang);
}
if(daijiaoguanliang<1){
jiaohuakaishi=0;//浇水完毕后跳出浇水循环
EEPROM.write(14+jilusuoyin*25, 49);//将本次浇花已完成写入EEPROM
digitalWrite(diancifa, HIGH);//关闭电磁阀
leijiliuliang=0;//清空累计流量
gps.begin(19200);//重新打开软串口
}else digitalWrite(diancifa, LOW);//累计流量小于设定流量,打开电磁阀
//正常的浇花部分
//浇花主程序///////////////////////////////////////////////////////////
//每秒频率测定,实时流量计算,累计流量叠加
if(millis()-meimiaoyilunhui>1000) {
meimiaoyilunhui=millis();//每一秒还原一次
shishiliuliang=jsqLLx/7.5;//计算实时流量(公式:频率=7.5*流量(L/min))
leijiliuliang=leijiliuliang+(shishiliuliang/60);//(每秒积分)计数累计流量
guzhangjiancejsq=guzhangjiancejsq+jsqLLx;//累加频率的数量,来确定是否故障(停水了)
jsqLLx=0;//清空每秒频率
#ifdef tiaoshixinxi2
Serial.print("yijiaoguanliang=");
Serial.print(yijiaoguanliang); //已浇灌量
Serial.print(" daijiaoguanliang=");
Serial.print(daijiaoguanliang); //待浇灌量
Serial.print(" shishiliuliang=");
Serial.print(shishiliuliang); //实时流量
Serial.print(" leijiliuliang=");
Serial.println(leijiliuliang); //累计流量
Serial.println(tempx); //now time
for(int col=0;col<14;col++)Serial.print(EEPROMtemp[col]);//set time
int x=EEPROMtemp[14];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[16];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[18];
Serial.print(x);
Serial.print(",");
x=EEPROMtemp[20];
Serial.print(x);
Serial.print(",");
Serial.print(EEPROMtemp[22]);
Serial.print(" V=");
Serial.print(EEPROMtemp[16]*EEPROMtemp[20]);
Serial.print(" L Time =");
Serial.print(EEPROMtemp[14]*EEPROMtemp[18]/6/24);
Serial.println(" Day");
Serial.println();
#endif
}else{
int liuliangjidianping =digitalRead(liuliangji);//读取流量计的电平
if(liuliangjidianping == HIGH && !chongfupanduanA){ //高电平 且 本次没计数,就计数
jsqLLx++;
chongfupanduanA=!chongfupanduanA;//本次已计数,则标记已计数
}
if(liuliangjidianping == LOW && chongfupanduanA)chongfupanduanA=!chongfupanduanA;//低电平,且 计数标记为1,则重置计数标记
}
//通过3秒的频率次数的累加,来判断是否停水了(防止电池阀在没水的时候长期运行)
//停水状态的应急处理
if(millis()-guzhangjianceA>9000) {
guzhangjianceA=millis();//同时清空故障检测的时间,否则一开始就是故障状态
if(guzhangjiancejsq<63){//3秒累计频率小于21,(9秒63),则说明流量小于1L/min,这个时候认定为停水了
//对浇花部分进行操作(即停止浇花)
digitalWrite(diancifa, HIGH);//关闭电磁阀
//leijiliuliang=0;//清空累计流量,这步没必要,因为只要不断电,来水之后是可以接着计算的
jiaohuakaishi=0;//跳出浇水循环
for(int col=0;col<12;col++){
Serial.println("GAME OVER");
delay(1000);//对应一个延迟,1小时
}
}
guzhangjiancejsq=0;//累计频率置0
gps.begin(19200);//重新打开软串口
}
}
|
|