系列文章目录
👇这是我之前写的技术指南1
家园防线 | 斐乐的手敲大型庭院物联网灌溉系统(持续更新)
文章目录
系列文章目录前言一、技术细节1)对传感器进行mobus轮询2)电路实现3)元器件图标 二、Arduino代码易错点(软件)1)时间范围选择2)工作次数控制3)变量销毁4)计时器5)项目解耦和逻辑执行6)注意代码中的逻辑重复7)millis()相关处理(1.想销毁不能简单赋值为0,delete也不行(创建也要用new int,不能直接int)2.这虽然是个值,但是不能使用减法减少值的大小 8)下一次循环还能返回到这里吗? 三、感觉自我的不足之处1.缺少测试的相关知识2.在项目构建过程中缺少可升级的规划 总结前言
我爱我家
在之前的文章里面我总是说技术向的东西,这次我想谈谈改革和方法论。
我在一家保安公司的科技部门工作,说到改革,我很喜欢之前开会的时候领导提出的5个基本要点,创新、共享(可以引申为非零和博弈)、敢于突破、唯实唯先等等,一说一,这很好地描述了改革的精神。
虽然整个系统都是我自做的,但是在改革的过程中还是遇到了阻力,整个系统虽然是一个对已有生产方式改进的过程,但是使用者在我推进的时候仍有怀疑。有时候水小了,有时候觉得这个东西过于复杂。家里如此,单位中推进的改革难度只会更大。
提示:以下是本篇文章正文内容,下面案例可供参考
一、技术细节
1)对传感器进行mobus轮询
之前一直以为是ESP32直接对传感器进行访问,但事后来发现好像不是这样的
其实是esp32通过rx,tx串口和传感器通讯,相当于一问一答。
ESP32实现需要配合相应的rs485转ttl转换器,我买的是这个:
轮询实现代码:
float getTemp(String temp){int commaPosition = -1;String info[9]; // 用字符串数组存储for (int i = 0; i < 9; i++){commaPosition = temp.indexOf(',');if (commaPosition != -1){info[i] = temp.substring(0, commaPosition);temp = temp.substring(commaPosition + 1, temp.length());}else{if (temp.length() > 0){// 最后一个会执行这个info[i] = temp.substring(0, commaPosition);}}}return (info[3].toInt() * 256 + info[4].toInt()) / 10.0; 这里传回的是一个整数,我们需要小数}void check_soil() //检测土壤湿度{delay(500); // 放慢输出频率for (int i = 0; i < 8; i++){// 发送测温命令tempSerial.write(item[i]); // write输出// Serial.println(item[i]);}delay(100); // 等待测温数据返回data_soil = "";while (tempSerial.available()){//从串口中读取数据// 这里读不出数据unsigned char in = (unsigned char)tempSerial.read(); // read读取// Serial.print(in, HEX);// Serial.print(',');data_soil += in;data_soil += ',';}if (data_soil.length() > 0){//先输出一下接收到的数据// Serial.println();// Serial.println(data);soil_moisture = getTemp(data_soil);Serial.print(soil_moisture);Serial.println("%water");}}
2)电路实现
只有一点要讲,两个设备都要接地,不要把两个接地线都链接到一起,而是应当把其都连到接地线上!
3)元器件图标
如果一个元器件写着接地,不代表其应当接到正极,而是代表要接地!写什么你就怎么做!
二、Arduino代码易错点(软件)
完整代码请见:HomeFront
1)时间范围选择
这里用了一种笨办法,自己处理数字,但是一定要找好两个地方的时间数值的关系,把握好与和或的关系
bool time_plus_check(int wat_begin_hour,int wat_begin_min,tm timeinfo){//这个gap不能超过30!不能让客户操作这个数值int time_gap =7; //在五分钟内必须进行反应if (wat_begin_min <time_gap){//?对时间状态进行修改if((timeinfo.tm_hour==wat_begin_hour-1 && timeinfo.tm_min>wat_begin_min-time_gap+60) || (timeinfo.tm_hour==wat_begin_hour && timeinfo.tm_min<wat_begin_min+time_gap)){return true;}else{return false;}}else if(time_gap+wat_begin_min>60){if((timeinfo.tm_hour==wat_begin_hour && timeinfo.tm_min>wat_begin_min-time_gap) ||(timeinfo.tm_hour==wat_begin_hour+1 && timeinfo.tm_min<wat_begin_min+time_gap-60)){return true;}else{return false;}}else{if((timeinfo.tm_hour==wat_begin_hour && timeinfo.tm_min>wat_begin_min-time_gap) &&(timeinfo.tm_hour==wat_begin_hour && timeinfo.tm_min<wat_begin_min+time_gap)){return true;}else{return false;}}
2)工作次数控制
在对工作次数的控制上:如若工作三次,从3开始,停止时间应当是0而不是-1。如果是-1就会导致多来了一次
这里用了debug的笨办法:疯狂输出值的大小,不管什么时候都要敢于debug,不能害怕
。
(Arduino有一个可以专门看电力的debug元器件的,但是没用过,好像叫stlink,可以试试看)
if (*time_flag > delaytime*(4-work_times)) // 1秒 = 1000 毫秒{//fixme:这里是不是没有做减法?建议调试之后再操作这里work_times = work_times - 1;}if (work_times == 0) //不能是-1否则会在最后一个引脚多执行一次{for (i = 0; i < length(working_solenoid_valve); i++){working_solenoid_valve[i] = 0;}// solenoid_valve4 = 0;solenoid_line = "nothing";// Serial.print("n1");// Serial.print(pump_working_flag);pump_working_flag = 0;// Serial.print("n2");// Serial.print(pump_working_flag);hand_watering_flag = 0;// Serial.println("1cnm");soil2wat=0;// Serial.println("2cnm");// time_flag = 0;mid_time = 0;// Serial.println("3cnm");// Serial.println("1bg");delete time_flag; //&返回地址,*返回所对应的值// Serial.println("4cnm");int* time_flag = new int(0);// *time_flag = 0;// Serial.println("1fn");}
3)变量销毁
在c++和Arduino中,只有new出来得变量才可以使用delete进行销毁,不是new的都是自动回收的
生成变量
int* time_flag = new int(0);//必须这样做否则没法delete
销毁(必须delete 指针),对于millis()出来的数值,你粗暴地将其赋予0的值也是错的。
delete time_flag; //&返回地址,*返回所对应的值// Serial.println("4cnm");int* time_flag = new int(0);
4)计时器
如果在使用很短的时间进行代码测试的话,因为延时的时间长度不一样,由于某些动作开始时间都是很早了,会导致启动地第一个动作地时间很短。
如果执行地时间比较长的话之前延时的长度几乎可以忽略不计。
5)项目解耦和逻辑执行
不要在做一个函数的时候想着顺带着把别的操作也做了,最好就是一个管一个的,想在这里,我在应当返回否的状态下又处理了pump_working_flag是很有问题的。
因为后面的代码是否执行浇水判断是这样的
if ( ( (time2go() || soil_go()) && auto_watering_flag == 1 ) || hand_watering_flag == 1) // fixme:time2go可能需要再大一点;1.算好delay和time2go;2.做一个每天几次,或者上下午几次的东西//这里如果auto_watering_flag==1在前面,当其为0的时候time2go()和soil_go()不会执行了,所以应该放在后面
这里如果auto_watering_flag==1在前面,当其为0的时候time2go()和soil_go()不会执行了,所以应该放在后面
常用的在前,偶尔使用的应当在后面
最后在这里是一个或的关系,如果下面的代码这样写,就可能存在在否定的状态下一棍子把别人敲死的可能性。 逻辑表达式不是全部执行完的,它如果看前面执行了之后后面的值怎么都不可能为真,就会不再执行。
u1s1,逻辑和控制一定要分开,否则很容易出错
bool soil_go(){check_soil();if (soil2wat==1){//表示目前水还在浇水return true;}if (soil_moisture_need>soil_moisture &&auto_watering_flag==1){pump_working_flag = 1;*time_flag = millis();if (soil2wat==0){work_times = 3; soil2wat=1;}return true;}else{// pump_working_flag = 0;这样写有很大的问题!return false;}}
所以,宁愿多写几个函数,也不要想一口吃成一个大胖子了。
6)注意代码中的逻辑重复
我其实不建议这样写,但是即使写了也一定要注意后续出现的情况中有无含有相同字母导致重复的情况,这里容易出错的。
else if(ch.lastIndexOf('%')!=-1){//读取你所需要的最低湿度Serial.println("lowest wet check\n");soil_moisture_need=ch.substring(0,ch.length()-1).toFloat();}else if(ch.lastIndexOf('x')!=-1){//!读取你所需要的最少时间,小心字母重复!Serial.println("least time check\n");delaytime=ch.substring(0,ch.length()-1).toInt()*60000;//fixme:这个时间不是很对的,需要测试的时候得到相关时间的单位然后进行修改!Serial.print(delaytime);carwash_time=delaytime*2;}else if (ch.lastIndexOf(':')!=-1 && ch.lastIndexOf('v')!=-1){//应当输入5:31v
7)millis()相关处理(
“被赋予millis()的变量就像是一辆末日战场,无法阻止或削减,销毁它是唯一的方案”
1.想销毁不能简单赋值为0,delete也不行(创建也要用new int,不能直接int)
2.这虽然是个值,但是不能使用减法减少值的大小
所以要使用这样的判断模式:
if (*time_flag > delaytime * (4 - work_times)) // 1秒 = 1000 毫秒{// fixme:这里是不是没有做减法?建议调试之后再操作这里work_times = work_times - 1;}if (work_times == 0) //不能是-1否则会在最后一个引脚多执行一次{shut_all();hand_watering_flag = 0;}
8)下一次循环还能返回到这里吗?
由于arduino的loop(),当你达到一个状态的时候,由于状态是可持续的,对于状态来说极有可能在下一次再次达到触发条件,所以要有一个在哪个状态的判断条件,避免导致"交易重放"
bool time2go(){if (auto_watering_flag == 1){if (work_times > 0){//如果手头上还有工作先做掉return true;}
三、感觉自我的不足之处
1.缺少测试的相关知识
2.在项目构建过程中缺少可升级的规划
比如在埋线的时候,忘记再加一根线,本来可以对后续的升级和优化做准备
总结
勇于实现,知性合一。
如果觉得《家园防线 | 全栈物联网灌溉系统技术指南》对你有帮助,请点赞、收藏,并留下你的观点哦!