小学期一门课名为硬件综合训练,一直到了第二周才开课,一开始我还以为又是什么水到不行的课,交个报告就完事。当我见到那物联网教学科研平台时,就改变了我的看法。六个传感器,一个LINUX网关,传感器通过ZIGBEE网络发送数据,网关接收并用图形化界面显示出来。而我们要做的,就是在此基础上模拟一些功能,使其能用在智能家居上。就比如有个温湿度传感器,我们可以利用这个传感器对家庭的温湿度进行监控,一旦发现家里温度过高,就可以控制空调自动开启。这是一个计算机科学与技术专业的软件狗第一次接触到硬件的过程。(据说,全国高校本专业只有本校开设了这门课。。。)
闲话就说到这里,今天距离第一次课刚好一周,距离我拿到实验平台共计四天的时间,对平台也熟悉的差不多了,经过一次次折腾,大致了解了Zigbee网络的工作方式,以及代码写法,明天就可以开始真正的开发智能家居的应用。
7月7号下午:
这不就是一个已经可以使用的平台了吗,我们不用在对硬件进行编码了吧,我只要把传感器采集到的数据通过串口读取到我的电脑上不就完了。(ps:平台网关和Zigbee协调器模块是通过串口进行通信的)网关是up-cup6410型号,传感器均来自于北京博创兴盛科技有限公司。百度搜了C语言读串口的代码,其实这个还真不好搜,费了好大劲才找到,可能是我关键字不太对吧,也可能是我对c语言理解不够深刻,搜到了说直接用CreateFile把串口当成文件来读的。可是细数我两年的编程经历,读文件还只用过FILE类。。。这个部分贴个代码,方便跟我一样的初学者。
<span style="font-family:KaiTi_GB2312;font-size:18px;">HANDLE test = CreateFile(_T("COM3"), GENERIC_READ, 0, NULL, OPEN_EXISTING, NULL, NULL); //打开串口 if (test == INVALID_HANDLE_VALUE) //打开失败 { cout << "open filed\n"; system("pause"); exit(-1); } DCB *dcb = new DCB(); //设置串口状态所用的控制块 GetCommState(test, dcb); dcb->BaudRate = 115200; //波特率 dcb->ByteSize = 8; //字长 dcb->Parity = 0; //奇偶校验方式 dcb->fOutxCtsFlow = false; //无硬件流中断 SetCommState(test, dcb); //设置串口状态 DWORD readSize; DWORD fileSize = 1; char *buffer = new char[fileSize]; char *data = new char[17]; ReadFile(test, buffer, fileSize, &readSize, NULL); //从串口读数,test是串口的句柄,buffer数据缓冲区,filesize要读取的大小,readsize实际读取大小</span>
读完串口后数据即存放在buffer中。
找代码找的心力交瘁,然后开始进行测试,代码运行好久,控制台并没有输出,难道是波特率出错了(PS:后来才知道波特率不对也会有输出,只不过是乱码),然后我就用几个常用的波特率重新测试,可是依然没有输出。然后,我想到了那个帮助光盘,可是我电脑光驱位早被我拆了装了SSD,只能等同学回来了。后经同学提醒,要先往传感器里写程序,然后根据教程来做实验。
PS:后来才知道,这里我犯了两个错误,第一,filesize设置太大,串口发送速率太小,所以导致要读满整个buffer要很长时间。第二,ReadFile()的返回值判断错误。调用成功,返回非零,不成功返回零!!!我整个搞反了,所以导致我无论怎样都不会有输出。
7月8日:
终于拿到了文档,实验平台配套工具里面有一个叫做仿真器的东西,就是往传感器上写程序用的,按照教程里说的安装驱动方式提示找不到,打开所在目录发现驱动只适用于xp系统,没办法,要调试程序还得在虚拟机里装个xp系统。
装完系统,半个上午就过去了,再上一节课,一个上午就过去了,真正做事情的时间还是在下午。打开教程,浏览一下目录,先看一下开发环境。原来还需要一个叫IAR的IDE才能开发程序。还好有虚拟机,不用在我本机上折腾了。配好开发环境,跟着教程写完了我的第一个嵌入式程序,就是控制板子上的LED灯的,根据教程一步步烧到板子上,很开心。虽然对实际要做的没什么帮助,但是毕竟第一步跨了出去。看到目录共计二三十个实验,我挑选了三个实验来做,一个基础实验,一个传感器模块实验,一个无线通讯实验。几个实验没什么好说的,跟着教程做。
这三个实验均用到了xp系统的超级终端读串口数据,当我做完这几个实验,猛然想到,既然超级终端可以读出串口数据,那么我写的程序一定也能读出来。回到win10系统,修改一下buffer大小,这里修改是因为此时发送的数据少,并不是我意识到了数据采集和传输速率的差异。拿过来测试,还是不行,然后我就发现了返回值的问题,改了返回值之后,果然,是可以输出的。然后我好像突然想起了什么,心中一惊。这么说来,昨天这个程序也是可以读取数据的,只不过我把返回值弄错了。卧槽,传感器上的程序已经被我改了,这意味着现在的几个传感器已经不能组成一个网络了。卧槽,这真是作孽呀,别急,一定有办法的,一定会有原来程序的代码,明天再找。
7月9日
早早的起床,在光盘里找到了另一份教程的文档,卧槽,竟然有三十多页,密密麻麻的全是字和代码。不急,慢慢看,下面有界面,怎么跟之前看到的不一样呀。先不管了,就当这个是吧,找到源代码,烧进班子里面,每个都烧一份,插在实验平台上,我多希望它能工作呀,可是显示屏上还是空白。一定是哪里出了问题,继续看。下面有一行,把核心板以coordinator方式进行编译,温湿度传感器以router方式编译,其他传感器以Enddevice方式编译,还要找到SampleApp_Init()函数,把当前传感器类型的注释打开,不是当前传感器的类型全注释掉,重新烧进去,依然不行。看着文档,我实在不知道哪里出错了。算了,放弃吧,重新写一个网关出来,反正温湿度的无线数据传输已经会做了,把其他几个传感器加上应该也不难。
其他传感器前面也有实验,把代码复制到无线数据传输这来,原来核心板向串口发送数据是通过Uart_Send_Strirng()来发送的,在每一块前面加上对传感器类型的判断,恩,应该差不多吧。然后把程序写进传感器里。打开所有开关,串口插上,并没有数据。
算了,心累,明天继续。
7月10日
忘记昨天晚上做了什么梦,反正就是梦醒之后我非常清晰的认识到,昨天找的那个代码百分之百就是在课堂上看到的程序的源代码。一定是哪里有问题,肯定是昨天犯冲。把传感插上,程序重新写一遍,确认没有任何错误,连接好所有线,打开开关,依然没有任何东西出现,我有点慌,回去重新看看代码,还是没错,再回头看看屏幕,回头看看屏幕,卧槽,竟然出现了协调器结点,虽然只是一个孤零零的结点,至少出现了,这证明我的想法是对的。并不知道为什么,把所有结点重启一遍,屏幕上又是空白了,,,难道,是我昨天等的时间不够久??这也是太操蛋了,整个硬件实验把实验箱拿回来在宿舍做这种事情真操蛋。终于好了,五个传感器结点出现在了屏幕上,鉴于有毒气体的传感器用不上,以后的实验就把它抛弃掉好了。
测试串口,这么多天,又回到了原点,通过串口从传感器采集信息。插上串口,才想起来串口的一些属性不知道怎么设置,看看文档,里面没有说。。。不管了,跟无线实验那次用同样的属性好了,设置好波特率,奇偶校验等,打开程序,全是乱码。这怎么回事,它怎么发送数据的,打开源代码,搜索Uart_Send_Strirng(),竟然无结果。那这个是怎么发送数据的,看文档,原来是用的Hal库,向串口发送数据的函数变成了HalUARTWrite(),这样的话,波特率设置在哪,搜索一切跟波特率有关的名词,没有任何结果。百度搜索Hal库的初始化,也没有找到有用的信息。只能祭出大招,暴力循环,把几个常用的波特率测试一遍,终于找到了一个不是乱码的,波特率是115200,竟然是这么大的波特率,也是没想到。一切都通畅了,剩下的就是对数据进行解析,去除无用的数据,只留下我能用的到的数据。经过层层筛选,选出来传感器结点上线,下线,温湿度传感器周期性采集信息,其他传感器中断方式发送信息这么几条数据。代码比较简单,没什么好说的。
实验还有一个要求,搭建一个网站,接收传感器数据,然后手机端从网站获取数据。想了很久,想到了之前用过的html+nodejs+mongodb的一个网站模板,利用post请求发送数据,数据存放在数据库中,然后前端从数据库获取数据显示出来。既然这样,C语言写HttpPost请求并不是那么方便,于是转换成了C#,毕竟以前写过,熟悉。顺便贴下代码
<span style="font-family:KaiTi_GB2312;font-size:18px;">public static void httpPost(string name, string addr, string parentAddr, string state, string data) { Encoding dataencode = Encoding.UTF8; string url = "http://localhost:3000/insert"; HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; string parastr = "name=" + name + "&addr=" + addr + "&parentAddr=" + parentAddr + "&state=" + state + "&data=" + data; byte[] bytearr = dataencode.GetBytes(parastr); request.ContentLength = bytearr.Length; Stream newStream = request.GetRequestStream(); newStream.Write(bytearr, 0, bytearr.Length); newStream.Close(); }</span>
毕竟只是发送数据,不用接收网站返回的数据,所以就不用httpwebresponse了。启动网站,还要修改一下后台接收post的处理程序。第一次用mongodb,自然离不开百度,新建一个表,把之前的表全部替换成现在的表,果然没问题了,我真机智。
7月11日
修改了一下昨天对于网页操作的瑕疵,然后剩下的就是安卓获取网页数据并且进行处理了。并没有什么难度,上午还上了一上午的课,就没有了动力。我这个人就是奇怪,对于自己不会做的事情积极性特别高,但是如果一件事情看上去就知道肯定可以做出来的话,就完全进入了懒散状态,什么都不想干。
但是这毕竟还是作业呀,明天再写吧。
7月12日
安卓端开发。从本机或者网页与安卓端通信,好多种方式,不知道选哪个,好纠结。更纠结的是如果答辩老师问我为什么选了这种方式我怎么回答,反正我现在能想到回答方式就是:方案有好多种呀,我只是从中随便选择了一种,不能一直纠结选哪种方案不是。
好吧,我选的是安卓APP访问网页,获取数据,而不是从本机往安卓发数据。至于为什么呢,一个原因就是前段时间刚写完课表,一些东西还能用的上,就这么简单。
但是我也不知道做什么死,偏偏没有用上一次的网页请求代码,好像是当时我觉得上一次的http请求只有POST和GET两种方式,而我要访问的网页只是一个静态网页,并没有POST和GET表单能提交的地方,所以就不能用POST/GET,然而百度搜到的全是POST/GET方式的http请求。然后我就在想,我这种请求方式应该是什么呢,然后我就去用HttpWatch看了一下,妈呀,竟然是GET。(只能说我水平不行,对http了解不透彻,可是有那么多东西要学习,我又怎能在两年的时间里做到样样都了解呢,大部分还是一边做一边百度的。我一直在强调自己只有两年的编程经验,是从我敲第一个helloworld开始算起的,做到现在这样不知道算不算好,但是我自己是知道,我能做到更好的,只是内心依旧存在贪嗔痴。)
知道了是GET方式,我还是没有用上次的代码,不知道自己怎么想的,这次的HTTP请求代码是这样的
HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet("http://192.168.1.50:3000"); try { HttpResponse httpResponse = httpClient.execute(httpGet); if(httpResponse.getStatusLine().getStatusCode() == 200) { result = EntityUtils.toString(httpResponse.getEntity()); //seeString(result); Message msg = new Message(); msg.what = 0; msg.obj = result; handler.sendMessage(msg); } } catch(Exception e) { }
大概是觉得明明这么几行就可以解决的事情没必要多加几个包吧。第一次写的时候我是直接把http请求写入了Activity的onCreate方法里,然后调试,一直抛出异常,百度了才知道,原来Android4.0之后http请求必须新开线程执行,否则就会异常,而且Manifest.xml里还要加上<uses-permission android:name="android.permission.INTERNET" />。这才想起来上次那个本来就是异步http请求包,怪不得之前没有遇到这个问题。继续往下,因为要做实时监控的系统,所以就要每隔一段时间获取一下数据,我就又开了一个线程来控制这个逻辑,这个线程的作用就是每隔一段时间创建一个线程去发出http请求获取数据。
继续调试,发现每获取一次数据之后,去更新UI的内容的时候又会抛出异常,百度搜了异常,才发现Android是不允许线程更改UI的,如果要更改UI的话只能用其他的方法,这里能搜到好几种,我选用了其中一种handler的方法。
具体工作方式也没有了解的很细致,我只说一下我遇到的问题,使用了Handler方法之后依然是抛异常退出,使我百思不得其解。最后才想到是线程嵌套层数太多了,我是用的线程的线程去更改的UI,这是不可行的,于是,我就在控制逻辑的线程里直接写了http请求,然后可以工作。
完美的一天。