2017-12-10 21:37:27 ZenNaiHeQiao 阅读数 7239
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3424 人正在学习 去看看 朱有鹏

前言:

1:要实现单片机实现基础的网络功能,首先Ping 的通,能够进行TCP的自收发。此时才能继续上层开发的websever。
此时分为两层,底层能够进行正确的数据交换。
顶层需要做的是对报文的解析和编辑加工。

本人参考的是网络模块(W5300 和W5500),以及其例程。

2:首先我们了解一下w5500提供 的例程里面有:
        (1)Network install 
(2)DHCP   向主机获取动态IP
(3)TCP Server 
(4)TCP Client
(5)UDP 
        (6)DNS    暂时不知道怎么用
(7)SMTP  简单的邮件传输协议
(8)HTTP Server  
(9)HTTP Client
(10)Net Bios  局域网内可以使用个API,
(11)NTP    同步网络时间的协议  
(12)ping
其中感觉有进一步研究价值的,SMTP可以实现向使用者发布信息,其实就是一个单片机发邮箱的过程。
HTTPserver配置参数,程序更新。这个最好结合IAP做一个方便控制,易于升级的好程序还是挺好的。
Http/Client 接受电脑端的控制。  之前做过XML控制的从机设备。        
NetBios可以给设备起个别名,不必在输入IP.http://bbs.elecfans.com/jishu_470902_1_1.html
NTP同步时间。这个没用过,但是设备间同步时间应该还是蛮重要的。

3:websever通讯的过程。
HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:

(1)建立TCP连接

在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能,才能进行更层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80

2)Web浏览器向Web服务器发送请求命令<request-line>

一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令

例如:GET/sample/hello.jsp HTTP/1.1

(3)Web浏览器发送请求头信息<headers>

浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行<blank line>来通知服务器,它已经结束了该头信息的发送

(4)Web服务器应答

客户机向服务器发出请求后,服务器会客户机回送应答,HTTP/1.1 200 OK,应答的第一部分是协议的版本号和应答状态码

5)Web服务器发送应答头信息

正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。

(6)Web服务器向浏览器发送数据

Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据

(7)Web服务器关闭TCP连接

一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码Connection:keep-alive

TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

   4: 如下是HTTP的请求格式:
\r 代表回车,\n代表换行 




举个实例:
请求:
POST /index.html HTTP/1.1
HOST: www.XXX.com
User-Agent: Mozilla/5.0(Windows NT 6.1;rv:15.0) Firefox/15.0

Username=admin&password=admin
响应:
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
</head>
<body>
    <p>this is http response</p>
</body>
</html>


请求方法:

(1)GET方法意思是获取URL指定的资源,这个请求方式是最简单的也是最常用的。使用GET 方法时,可以将请求参数和对应的值附加在 URI 后面,利用一个问号(“?”)将资源的URI和请求参数隔开,参数之间使用与符号(“&”)隔开,因此传递参数长度也受到了限制,而且与隐私相关的信息也直接暴露在URI中。比如/index.jsp?username=holmofy&password=123123

(2)HEAD 方法与GET用法相同,但没有响应体,使用场合没有GET多。比如下载前使用HEAD发送请求,通过ContentLength响应字段,来了解网络资源的大小;或者通过LastModified响应字段来判断本地缓存资源是否要更新。 
(3)PUT:用于向指定资源位置上传其最新内容(原来没有就上传,有就上传并覆盖原来的内容) 

GET和POST是最常用的

Request-URI
URI 的最常见形式是统一资源定位符 (URL),它也被称为 Web地址。这种网址是我们经常见到的。
http://example.org/absolute/path/resource.txt
在请求的URI中还有一种相对路径的,对于这种请求来说在请求头信息中一般有Host值来指定服务器主机。比如:
GET /Test.jsp HTTP/1.1
...
Host:www.example.com
...

Http响应码

HTTP/1.0 200 OK

这个状态码是200,表示请求成功。HTTP协议中状态码有三位数字组成,第一位数字定义了响应的类别,有以下五种:

  • 1XX:信息提示。表示请求已被服务器接受,但需要继续处理,范围为100~101。
  • 2XX:请求成功。服务器成功处理了请求。范围为200~206。
  • 3XX:客户端重定向。重定向状态码用于告诉客户端浏览器,它们访问的资源已被移动,并告诉客户端新的资源位置。客户端收到重定向会重新对新资源发起请求。范围为300~305。
  • 4XX:客户端信息错误。客户端可能发送了服务器无法处理的东西,比如请求的格式错误,或者请求了一个不存在的资源。范围为400~415。
  • 5XX:服务器出错。客户端发送了有效的请求,但是服务器自身出现错误,比如Web程序运行出错。范围是500~505。

常见的状态码务必要熟悉:

200:客户端请求成功。

302:重定向。

404:请求资源不存在。

400:请求语法错误,服务器无法理解。

403:服务器收到请求,但拒绝提供服务。

500:服务器内部错误。

503:服务器当前不能处理客户端请求,可能需要一段时间后才能恢复正常。

(1)如图键入地址时,要求输入登陆密码:


       实现过程:

服务器:输入地址和端口发送到内容为

GET / HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)
Accept-Encoding: gzip, deflate
Host: 192.168.0.101:5000
Connection: Keep-Alive
此时回复报文为
HTTP/1.1 401 Authorization Required
Server: MySocket Server
WWW-Authenticate: Basic realm=\"MXCHIP 3280\"
Content-Type: text/html
Content-Length: 169
<HTML>
<HEAD>
<TITLE>Error</TITLE>
<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">
</HEAD>
<BODY>
<H1>401 Unauthorized.</H1>
</BODY>
</HTML>

这个输入HTTP的基本认证。用于web浏览器或其他客户端在请求时提供用户名和密码的登录认证。

输入:账户和密码后收到的报文
GET / HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)
Accept-Encoding: gzip, deflate
Host: 192.168.0.101:5000
Connection: Keep-Alive
Authorization: Basic YWRtaW46YWRtaW4=

YWRtaW46YWRtaW4=  是admin  admin 经过 base64编码后 的数值。


常见的报文:

<!DOCTYPE html>
<html>
<title>LOGIN</title> 
<body>
<form method='get' action='Login.cgi'> 
NAME:<input type='text' name='username'><br> 
PASSWORD:<input type='password' name='password'><br> 
<input type='submit' value='LODA'>
</form>
</body>
</html>



突然发现WIZ 官网上竟然有好多资料。下面是一个web的资料, Mark一下。
http://wizwiki.net/wiki/doku.php?id=products:wiz550web:wiz550webgsg_en#basic_demo_web_pages



这世间最难的事情莫过于把事情讲清楚吧!
2015-01-03 13:00:27 wenyuexunyin 阅读数 25320
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3424 人正在学习 去看看 朱有鹏
在阅读本教程时,假设你已经

1、能够用单片机驱动网络模块(如W5100、ENC28J60),实现最基本的socket连接和数据收发。如果你没有驱动代码,请在百度中搜索下载。
2、了解TCP/IP协议。如果不了解,请查看谢希仁《计算机网络(第五版)》或其他相关书籍。
3、会用html语言编写(简单或复杂)网页。如果不会编写,请点击->  http://www.w3school.com.cn 

一、通过浏览器向单片机发送请求

我们在浏览器地址栏输入地址并确定,实际上就是发送一个网页请求,现在,我们把单片机作为网页的服务器,那么如何接收并响应这个请求呢?

首先,将网络模块的某个socket设置为sever模式,并将此socket的端口设置为80(HTTP默认端口)。如果不想把端口设置为80行不行呢?当然可以,但是需要注意:
假设socket的地址为192.168.1.199,端口号80,在浏览器输入HTTP://192.168.1.199后,就可以直接向此socket发起连接,因为HTTP的默认端口会自动加到IP后面。
若此socket的端口为30000,那么输入地址的时候,就需要手动输入端口号,即:HTTP://192.168.1.199:30000。如果懒得输端口号,那就直接将端口设置为80吧。

二、浏览器请求解析

下面进行下一个准备工作:将网络模块 socket接收到的数据通过串口转发出来,并用串口助手观察。

在浏览器输入socket地址后,可以在串口助手上看到以下数据:(以下数据均为字符)


//------------------------------------------------

GET / HTTP/1.1
Host: 192.168.1.199
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8

//------------------------------------------------


这就是浏览器向服务器发送的请求数据(不同浏览器的数据稍有不同),关于这些数据的含义,请参看以下两篇文章:

->(文章1) http://www.cnblogs.com/loveyakamoz/archive/2011/07/22/2113614.html
->(文章2) http://canrry.iteye.com/blog/1331292

在这里我们只关心第一行,这行数据表明,浏览器向单片机请求网页。这是浏览器第一次请求的命令,若需要请求其他网页,第一行可能会变成:
GET /xxxx.HTML HTTP/1.1,这时,只要判断此处的信息,就可以知道该发送哪个网页给浏览器。

三、单片机返回相应网页数据包

假设收到浏览器第一次发送的请求“GET / HTTP/1.1”,此时浏览器和网络模块的socket已经建立连接,并等待数据返回。

我们通过相应socket向浏览器返回一个登陆界面 Login.HTML的代码:(下文相同代码均用(网页代码1)等方式表示)


(网页代码1)

//------------------------------------------------

<!DOCTYPE html>
<html>
<title>LOGIN</title> 
<body>
<form method='get' action='Login.cgi'> 
用户名:<input type='text' name='username'><br> 
密 码:<input type='password' name='password'><br> 
<input type='submit' value='登 录'>
</form>
</body>
</html>

//------------------------------------------------


因为单片机的储存空间有限,一般将网页以const的形式存放在flash中,而且尽量精简,除了必要的效果和兼容性语句外,

其他都可以删掉(如对齐格式时使用的大量tab、空格等),以减轻单片机负担。
但是为了编写网页方便,建议将网页源文件复制粘贴到别处,删除多余的内容后再烧写到单片机中。

好了,接着上面的说,真的只发一个网页过去就行了吗?

当然了,肯定不行。因为浏览器无法知道单片机要发送什么类型、多少长度的数据。所以,我们还需要一个返回数据的格式。


//------------------------------------------------

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: xx

//------------------------------------------------


文章1`2中也已经讲到了。所以,我们发送到浏览器的数据应该是这样的:


//------------------------------------------------

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length:282

(网页代码1)


//------------------------------------------------




注意,这些数据中,有些换行是不能省的(下文\r\n起强调作用,并不是接收到的字符):


//------------------------------------------------

HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length:xx\r\n
\r\n
(网页代码1)\r\n
\r\n

//------------------------------------------------


否则浏览器无法识别数据。

其中的Content-Length:282是网页文件的大小(字节),需要提前算好。
这样,浏览器收到了第一个网页,显示效果如下。



不管其他的,我们先登陆一下试试,输入用户名:hello,密码:world,点击登录。然后我们发现单片机收到了如下数据:


//------------------------------------------------

GET /Login.cgi?Username=hello&Password=world HTTP/1.1
Host: 192.168.1.199
Connection: Keep-Alive
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; InfoPath.2; SE 2.X MetaSr 1.0)
Referer: http://192.168.1.199/
Accept-Encoding: gzip, deflate

//------------------------------------------------


好了,我们需要的数据应该全在第一行里面了,有用户名和密码,但是看一下浏览器的地址栏,用户名和密码同样以URL的形式出现了:


//------------------------------------------------

HTTP://192.168.1.199/Login.cgi?Username=hello&Password=world

//------------------------------------------------


逗我呢这是,密码全看见了,有没有更为安全的方式呢,当然是有。首先看一下下面一篇文章:
->(文章3) http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html

知道了get 和 post 的区别,我们将Login.html中 method='get' 改为 method='post'重新操作一次,结果,单片机收到的数据变成了这样:


//------------------------------------------------

POST /Login.cgi HTTP/1.1
Host: 192.168.1.199
Connection: Keep-Alive
Content-Length: 29
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Accept-Language: zh-CN
Content-Type: application/x-www-form-urlencoded
Origin: http://192.168.1.199
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; InfoPath.2; SE 2.X MetaSr 1.0)
Referer: http://192.168.1.199/
Accept-Encoding: gzip, deflate\r\n
\r\n
Username=hello&Password=world

//------------------------------------------------


用户名和密码跑到了最后,浏览器地址栏也没有显示用户名和密码了,甚好!
以此方法,其他网页也可以发送到浏览器并返回单片机所需要的数据。

四、通过JS让网页活起来

现在让我们先做一个设置单片机串口的网页,并通过网页更新单片机串口设置。网页代码如下   

(网页代码2)

//------------------------------------------------

<!DOCTYPE html>
<html>
<title>COM SETTING</title> 
<body>
<form method='post' action='IP_Set.cgi'>
串口号:<input type='text' id='COM' name='COM_NUM'/><br>
波特率:<input type='text' id='BDR' name='BD_RATE'/><br>
<input type='submit' value='确定'/>
</form>
</body>
</html>

//------------------------------------------------


 先看一下效果:

串口的设置是在变的,不能写死到网页里,该怎么动态显示呢?
当然,这种时候少不了Javascript。在网页的最后追加几句话:

(网页代码3)

//------------------------------------------------

<script>
document.getElementById('COM').value='1';
document.getElementById('BDR').value='9600';
</script>

//------------------------------------------------


value里的值是通过单片机实时生成的,我们姑且这样写。发送一下试试先:


//------------------------------------------------

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length:410

(网页代码2)
(网页代码3)


//------------------------------------------------


现在,显示效果是这样的:


这样,我们完成了单片机内部参数在网页上显示的效果。

得陇望蜀,人之常情,做完串口配置,又想把所有的单片机配置全部用网页完成,结果做出来一个比较大的网页。

这个网页上需要显示很多的值,需要动态生成很多的JS语句,如果采用Content-Length这种固定长度的方式发送,该怎么计算网页长度呢?

当然,可以一句一句用strlen计算加上去,再更改Content-Length的值。
然而这种方式很麻烦,不好操作(因为网页是const类型),有时候网页的长度根本没办法预知,会导致网页发送失败,浏览器打不开网页。
实际上,这种问题当然是可以完全解决的。


五、灵活的网页传输方式


我们在上网的时候,实际上大部分的网页是动态的,不能预知长度,那么这些网页是怎么正确传输到浏览器上的呢?
如果你有TCP/IP的抓包软件,在抓到的数据包中,可能会看到这样一句:
Transfer-Encoding: chunked
这句话的意思是:不定长发送,即每个数据包中规定此包数据的大小,等待发送完成后,再发送一个结束符。
这样浏览器就不必知道整个网页有多大,只要等结束符就行。

具体操作方式是这样的:


//------------------------------------------------

HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Transfer-Encoding: chunked\r\n
\r\n
数据长度1(16进制)\r\n
数据1\r\n
数据长度2(16进制)\r\n
数据2\r\n
......
0\r\n
\r\n(结束符:0表示数据长度为0,换行后没有数据内容,再换行)

//------------------------------------------------


结束符前可以有任意多个数据。注意Content-Length方式的长度值是十进制,这种方式的长度值是十六进制。

那么再试一试上面的网页:


//------------------------------------------------

HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Transfer-Encoding: chunked\r\n
\r\n
12C\r\n
(网页代码2)\r\n
6C\r\n
(网页代码3)\r\n
......
0\r\n
\r\n

//------------------------------------------------


显示效果和之前的方法一样,但是这种方式操作起来就很方便了。
如果想向网页中添加数据,只要在结束符之前发送就OK。

现在,最基本的功能都实现了,但是,网页看起来有点丑,还能再给力点吗?

通过CSS格式,可以做出很多炫酷的网页特效,但是有些效果,还是不能代替图片。那么,看下一节:


六、图片,让网页绚丽多彩。


是的,少了图片的网页,看起来枯燥无味。那么,让我们尝试着发送一张图片到浏览器上去。


首先,需要把图片存入单片机,在百度中搜索“任意文件转C语言数组”,用这个小工具,把图片先转换成十六进制数组。


(剩下部分还在探索,文章中有一些小问题,需要验证后再做更改,待续...如有疑问或建议,请直接回复)
(原创文章,转载请注明出处)


2017-05-04 15:50:22 a1217158716 阅读数 1734
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3424 人正在学习 去看看 朱有鹏

本文用网页控制单片机的实例,实现了软件和硬件之间的沟通。

一、开发技术

硬件:430单片机;

软件:jsp,servlet。


二、开发思路

我们在430单片机上写上我们的程序,把单片机连接到串口上,能够通过串口精灵来控制430单片机。然后我们在servlet中开发Java控制串口的程序,该servlet相当于前面的串口精灵。通过点击网页上的不同数字,传递到单片机上,通过不同的数字来控制单片机实现不同的效果。


三、程序如下

430单片机程序

#include "msp430x14x.h"
int a=10;
unsigned char j=0x01,j2=0x00;
unsigned char j1=0X80,K1=0X01;
void delay(int i)
{
  while(i--)
  {
    for(int j=0;j<125;j++);
  }
}

void main( void )
{
  WDTCTL = WDTPW + WDTHOLD;   //停止看门狗
  P2DIR=0xff;   //把p2设为输出。
  P2OUT=0xff;   //把p2设为高电平
  P3SEL|=0x30;  
  P6DIR=0xff;
  P6OUT=0xff;
  ME1|=UTXE0+URXE0;
  UCTL0|=CHAR;
  UTCTL0|=SSEL0;
  UBR00=0x0D;
  UBR10=0x00;
  UMCTL0=0x6B;
  UCTL0&=~SWRST;
  IE1|=URXIE0;
  _EINT();
  while(1)
  {
    if(a=='0')    //实现流水灯。
    { 
      P2OUT=~j;
      j=j<<1;
      if(j==0X00)
          j=0x01;
      for(int i=0;i<10000;i++)
        for(int k=0;k<3;k++);
    }
    if(a=='1')    //实现碰撞灯

    {
      j1=j1>>1;
      K1=K1<<1;
      if(j1==0x01)
        j1=0x80;
      if(K1==0x80)
        K1=0x01;
      P2OUT=~(j1|K1);
      for(int i=0;i<10000;i++)
        for(int k=0;k<2;k++);
    }
    if(a=='2')     //实现闪光灯

    {
      j2^=0XFF;
      P2OUT=j2;
      for(int i=0;i<10000;i++)
        for(int k=0;k<2;k++);
    }
    if(a=='3')     //实现蜂鸣器

    {
      P6OUT^=0xff;
      for(int i=0;i<10000;i++)
        for(int k=0;k<2;k++);
    }
  }
}
#pragma vector = UART0RX_VECTOR   //中断服务子程序
__interrupt void shen(void)
{
  P6OUT=0xff;     
  a=RXBUF0;
  while(!(IFG1&UTXIFG0));
     TXBUF0 = a; 
     
}

Servlet程序

package filst;
import java.io.* ;
import java.util.*;  
import gnu.io.*;
public class Put{  
    InputStream inputStream; 
    OutputStream outputStream;
    SerialPort serialPort; 
    CommPortIdentifier portId;  
    int x0=0,x1=0,x2=0,x3=0;
    public void put(String name,int x0,int x1,int x2,int x3) {    //参数分别表示:串口号,波特率,数据位,停止位,校验位
        Enumeration portList = CommPortIdentifier.getPortIdentifiers();
        while (portList.hasMoreElements()) {  
            CommPortIdentifier temp = (CommPortIdentifier) portList.nextElement();  
            if (temp.getPortType() == CommPortIdentifier.PORT_SERIAL) {// 判断如果端口类型是串口  
                if (temp.getName().equals(name)) { // 判断如果端口已经启动就连接  
                    portId = temp;  
                }  
            } 
        }
        try {  
            serialPort = (SerialPort) portId.open(name, 2000);  
            inputStream = serialPort.getInputStream();  
            outputStream = serialPort.getOutputStream(); 
            serialPort.setSerialPortParams(x0,x1,x2,x3);  
        } catch (Exception e) {}  
       
    }  
  
    public void send(String content){     // 发送的内容。
        try {  
            outputStream.write(content.getBytes());  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
      
    public void ClosePort() {      //关闭串口。
        if (serialPort != null) {  
          serialPort.close();  
        }  
      }  
}

jsp程序

<%@ page language="java" contentType="text/html;charset=gb2312"%>
<body style="background-color:orange">
<center>
<form action="kang.jsp" method="post"><br><br><br>
<input type="radio" name="sh" value="0"/>流水灯   
<input type="radio" name="sh" value="1"/>碰撞灯   
<input type="radio" name="sh" value="2"/>闪光灯    
<input type="radio" name="sh" value="3"/>蜂鸣器<br><br><br>
<input type="submit" value="提交"/>
</form>
</center>
</body>

接受内容的JSP页面

<%@ page language="java" contentType="text/html;charset=gb2312"%>
<jsp:useBean id="student" class="filst.Put" scope="application"></jsp:useBean>
<jsp:useBean id="Shezhi" class="filst.shezhi" scope="application"></jsp:useBean>
<%
     student.put("COM3",9600,7,1,2);
     student.send(request.getParameter("sh"));
     student.ClosePort();
%>


2015-01-24 20:50:47 Pnoter 阅读数 8245
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3424 人正在学习 去看看 朱有鹏

一 · 在windows操作系统下,实现Java串口通信,需要用到sun提供的串口通信包,javacomm【可以去Java站点去下载最新版本的压缩包,然后解压】。

解压之后要用到压缩包中的三个文件,将这三个文件按照如下方式配置【一下路径为相对路径,视个人电脑Java安装路径不同而异】:

1、comm.jar放置到 JAVA_HOME/jre/lib/ext;【jre/lib(也就是在JAVA文件夹下的jre)】

2、win32com.dll放置到 JAVA_HOME/bin;

3、javax.comm.properties两个地方都要放;

二 · 在这里我们首先了解一下Java串口通信API说明【CommunicationAPI(包含于javax.comm包中)】,这样有利进一步了解Java串口通信包,ava提供了 CommunicationAPI(包含于javax.comm包中)用于通过与机器无关的方式,控制各种外部设备。Communications API,是标准的Java的扩展部分,它在JavaAPI中是没有附带的。

三 · Communications API 简介:

Communications API 的核心是抽象的CommPort类及其两个子类:SerialPort类和ParallePort类。其中,SerialPort类是用于串口通信的类,ParallePort类是用于并行口通信的类。CommPort类还提供了常规的通信模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,专用于与端口上的设备进行通信。然而,这些类的构造方法都被有意的设置为非公有的(non-public)。所以,不能直接构造对象,而是先通过静态的CommPortIdentifer.getPortIdentifiers()获得端口列表;再从这个端口列表中选择所需要的端口,并调用CommPortIdentifer对象的Open( )方法,这样,就能得到一个CommPort对象。当然,还要将这个CommPort对象的类型转换为某个非抽象的子类,表明是特定的通讯设备。该子类可以是SerialPort类和ParallePort类中的一个。下面将分别对CommPort类,CommPortIdentifier类,串口类SerialPort进行详细的介绍。 

四 · CommPortIdentifier类 :

addPortName(String, int, CommDriver) 添加端口名到端口列表里 
addPortOwnershipListener(CommPortOwnershipListener) 添加端口拥有的监听器 
removePortOwnershipListener(CommPortOwnershipListener) 移除端口拥有的监听器 
getCurrentOwner() 得到当前占有端口的对象或应用程序 
getName() 得到端口名称 
getPortIdentifier(CommPort) 得到参数打开的端口的CommPortIdentifier类型对象 
getPortIdentifier(String) 得到以参数命名的端口的CommPortIdentifier类型对象 
getPortIdentifiers() 得到系统中的端口列表 
getPortType() 得到端口的类型 
isCurrentlyOwned() 判断当前端口是否被占用 
open(FileDescriptor) 用文件描述的类型打开端口 
open(String, int) 打开端口,两个参数:程序名称,延迟时间(毫秒数) 

五 · SerialPort类:

DATABITS_5 数据位为5 STOPBITS_2 停止位为2 PARITY_ODD 奇检验 
DATABITS_6 数据位为6 STOPBITS_1 停止位为1 PARITY_MARK 标记检验 
DATABITS_7 数据位为7 STOPBITS_1_5 停止为1.5 PARITY_NONE 空格检验 
DATABITS_8 数据位为8 PARITY_EVEN 偶检验 PARITY_SPACE 无检验 
SerialPort对象的关于串口参数的函数 
getBaudRate() 得到波特率 getParity() 得到检验类型 
getDataBits() 得到数据位数 getStopBits() 得到停止位数 
setSerialPortParams(int, int, int, int) 设置串口参数依次为(波特率,数据位,停止位,奇偶检验) 
SerialPort关于事件的静态成员变量 
BI Break interrupt中断 FE Framing error错误 
CD Carrier detect载波侦听 OE Overrun error错误 
CTS Clear to send清除以传送 PE Parity error奇偶检验错误 
DSR Data set ready数据备妥 RI Ring indicator响铃侦测 
DATA_AVAILABLE 串口中的可用数据 OUTPUT_BUFFER_EMPTY 输出缓冲区空 
SerialPort中关于事件的方法 
isCD() 是否有载波 isCTS() 是否清除以传送 isDSR() 数据是否备妥 
isDTR() 是否数据端备妥 isRI() 是否响铃侦测 isRTS()   是否要求传送 
addEventListener(SerialPortEventListener)    向SerialPort对象中添加串口事件监听器 
removeEventListener() 移除SerialPort对象中的串口事件监听器 
notifyOnBreakInterrupt(boolean) 设置中断事件true有效,false无效 
notifyOnCarrierDetect(boolean) 设置载波监听事件true有效,false无效 
notifyOnCTS(boolean) 设置清除发送事件true有效,false无效 
notifyOnDataAvailable(boolean) 设置串口有数据的事件true有效,false无效 
notifyOnDSR(boolean) 设置数据备妥事件true有效,false无效 
notifyOnFramingError(boolean) 设置发生错误事件true有效,false无效 
notifyOnOutputEmpty(boolean) 设置发送缓冲区为空事件true有效,false无效 
notifyOnParityError(boolean) 设置发生奇偶检验错误事件true有效,false无效 
notifyOnRingIndicator(boolean) 设置响铃侦测事件true有效,false无效 
getEventType() 得到发生的事件类型返回值为int型 
sendBreak(int) 设置中断过程的时间,参数为毫秒值 
setRTS(boolean) 设置或清除RTS位 
setDTR(boolean) 设置或清除DTR位 
close() 关闭串口 
getOutputStream() 得到OutputStream类型的输出流 
getInputStream() 得到InputStream类型的输入流

六 · 看一段用Java写的一个往串口写(Write)字符串的完整程序【如果你的电脑没有那么多的commport可以去网上搜索一个虚拟端口的软件,在你的电脑上开几个虚拟的commport,推荐软件Virtual Serial Port Driver ,个人表示很好用,可惜是收费的,不过可以试用,你懂!】

如下为往我的电脑com1口发送字符串的实例【已经经过测试,完整无误!】

//添加类包
import javax.comm.*;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;

//创建一个类JavaSerialPort,通过关键词implements实声明自己使用Runnable和SerialPortEventListener这两个接口
public class myComPortServlet  {
	// 定义、声明变量
	private String appName = "Java串口通信";
	private int timeout = 2000;// 定义一个打开端口的最大等待时间
	public static String PortName;
	private CommPortIdentifier commPort;
	private SerialPort serialPort;
	private OutputStream outputStream;
	public static OutputStream out;
	public static String messageString = "点亮单片机上的第**个LED灯!";// 给选定端口发送的字符

	// listPort()方法的定义
	public void listPort() {
		CommPortIdentifier cpid;
		Enumeration en = CommPortIdentifier.getPortIdentifiers();
		//System.out.println("端口信息 :" + en);
		//System.out.println("端口列表如下:");
		while (en.hasMoreElements()) {
			cpid = (CommPortIdentifier) en.nextElement();
			if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				//System.out.println(cpid.getName() + ", "
						//+ cpid.getCurrentOwner());
			}
		}
	}

	// selectPort(String portName)方法的定义
	public void selectPort(String portName) {
		this.commPort = null;
		CommPortIdentifier cpid;
		Enumeration en = CommPortIdentifier.getPortIdentifiers();
		while (en.hasMoreElements()) {
			cpid = (CommPortIdentifier) en.nextElement();
			if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				if (cpid.getName().equals(portName)) {
					this.commPort = cpid;
				}
			}
		}
		openPort();
	}

	// openPort()方法的定义
	private void openPort() {
		try {
			serialPort = (SerialPort) commPort.open(appName, timeout);
		} catch (PortInUseException e) {
		}
		try {
			out = serialPort.getOutputStream();
		} catch (IOException e) {
		}
		try {
			serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
		} catch (UnsupportedCommOperationException e) {
		}
		try {
			out.write(messageString.getBytes());
		} catch (IOException e) {
		}
	}

	// checkPort()方法的定义
	private void checkPort() {
		if (commPort == null)
			throw new RuntimeException(
					"端口选择出错请用selectPort(String portName)方法选择正确的端口!");

		if (serialPort == null) {
			throw new RuntimeException("SerialPort 对象无效!");
		}
	}

	// write(String message)方法定义
	public void write(String message) {
		checkPort();

		try {
			outputStream = new BufferedOutputStream(
					serialPort.getOutputStream());
		} catch (IOException e) {
			throw new RuntimeException("获取端口的OutputStream出错:" + e.getMessage());
		}

		try {
			outputStream.write(message.getBytes());
			//log("信息发送成功!");
		} catch (IOException e) {
			throw new RuntimeException("向端口发送信息时出错:" + e.getMessage());
		} finally {
			try {
				outputStream.close();
			} catch (Exception e) {
			}
		}
	}

	// close()方法的定义
	public void close() {
		serialPort.close();
		serialPort = null;
		commPort = null;
	}

	// 类的主方法,Java程序运行首先是从这里开始的
	public static void main(String[] args) {
		myComPortServlet sp = new myComPortServlet();
		sp.listPort();
		sp.selectPort("COM1");// 选择COM1口交换数据数据

	}
}
七 · 我们再看一段用Java写的一个往串口写(Read)字符串的部分代码【只是在上面的程序中添加了几个方法,read()方法和serialEvent()端口监听方法以及run()计时方法】。
如下为读取我的电脑com2口字符串的实例【已经经过测试,完整无误!】
注意事项:关串口很重要,不然会出现运行错误【如下为close定义:
public void close() {
		serialPort.close();
		serialPort = null;
		commPort = null;
】。
	// 接收函数的定义
	public void startRead(int time) {
		try {
			inputStream = new BufferedInputStream(serialPort.getInputStream());
		} catch (IOException e) {
			throw new RuntimeException("获取端口的InputStream出错:" + e.getMessage());
		}
		try {
			serialPort.addEventListener(this);
		} catch (TooManyListenersException e) {
			throw new RuntimeException(e.getMessage());
		}
		serialPort.notifyOnDataAvailable(true);
		if (time > 0) {
			this.threadTime = time * 1000;
			Thread t = new Thread(this);
			t.start();
		}
	}

	// 端口事件监听函数定义
	public void serialEvent(SerialPortEvent arg0) {
		String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
		String dbURL = "jdbc:sqlserver://localhost:1433; DatabaseName=mytest";
		String userName = "sa"; // 默认用户名
		String userPwd = "9406"; // 密码
		Connection dbConn;
		switch (arg0.getEventType()) {
		case SerialPortEvent.BI:
		case SerialPortEvent.OE:
		case SerialPortEvent.FE:
		case SerialPortEvent.PE:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.RI:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			break;
		case SerialPortEvent.DATA_AVAILABLE:
			byte[] readBuffer = new byte[1024];
			try {
				while (inputStream.available() > 0) {
					inputStream.read(readBuffer);
					tempreture += new String(readBuffer).trim();
				}
				System.out.println(tempreture);
			} catch (IOException e) {
			}
			try {
				Class.forName(driverName);
				dbConn = DriverManager.getConnection(dbURL, userName, userPwd);
				if (!dbConn.isClosed()) {
					// pw.println("_>Succeed to connecting SQL!" + "
" + "
"); } Statement statement1 = dbConn.createStatement(); @SuppressWarnings("unused") Statement statement2 = dbConn.createStatement(); String sql1 = "INSERT INTO tempreture(tempreture) VALUES('" + tempreture + "')"; @SuppressWarnings("unused") int rs1 = statement1.executeUpdate(sql1); } catch (Exception e) { e.printStackTrace(); } } } // 计时函数 public void run() { try { Thread.sleep(threadTime); serialPort.close();// 关闭接收函数,端口关闭 } catch (Exception e) { e.printStackTrace(); } }




2017-05-01 12:42:15 u014364615 阅读数 3380
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3424 人正在学习 去看看 朱有鹏

为了适合需要,现在设备IP可能变化,以dev.fuhome.net域名所指向的IP为准。(其他域名以后仅作浏览网页使用,原IP暂时有效,有兴趣的可以根据本文内容做到再也不需要固定服务器IP了,很简单的哦,不信你看)

DNS,通俗讲就是将域名比如:www.fuhome.net,转换成服务器的IP地址,单片机也能轻松实现哦,很简单

原理:任何可以发送UDP数据包的设备,采用任何本地端口,向DNS服务器的53端口发送UDP DNS请求包即可收到返回。
DNS服务器的IP很多比如谷歌8.8.8.8,中国的114.114.114.114,成白上千,我们的手机,电脑,路由器会有一个DNS服务器IP设置向,默认是自动获取,那意味着变化。不过也可以用已知很出名的DNS服务器IP。
本文我做了一个很取巧的方式,把路由器的网关IP作为DNS服务器IP,因为路由器会主动帮我们发到真正的DNS上面,收到信息也会转发给我们:
下面是我请求的www.fuhome.net的例子(换行是为了让相应字节分开好讲解,收到的数据包是连续的)

1、DNS请求
12 34 01 00 00 01 00 00 00 00 00 00
03 77 77 77 06 66 75 68 6f 6d 65 03 6e 65 74 (就是这个意思:3 www 6 fuhome 3 net)
00 00 01 00 01

2、DNS接收
12 34 81 80 00 01 00 01 00 00 00 00
03 77 77 77 06 66 75 68 6f 6d 65 03 6e 65 74 (就是这个意思:3 www 6 fuhome 3 net)
00 00 01 00 01
c0 0c 00 01 00 01 00 00 01 2c 00 (C0 0C是一个标志,有用信息开始)
04 73 1c 5d c9 (04长度 后面四个就是服务器IP,自己用调试工具发送试试,看看是不是很神奇,有的网站可能还有别的内容,最后一行一般是04 开头的IP)

114.114.114.114作为DNS服务器
这里写图片描述

路由器网关IP:192.168.1.1作为DNS服务器,注意每个人路由器也许设置不一样,那要具体看你路由器的IP了
这里写图片描述

fu_dns.c,不同的硬件发送方式不用,原理是一样的

“`
/**********************************************************/
/* 本程序仅供学习参考,未经作者允许,不得用于任何商业用途 */
/* DNS相关 */
/* 本程序用于硬件设备接入fuhome.net 平台进行远程控制(手机APP/微信/WEB */
/* 版本:v2.0 2017年3月17日 */
/* 龙剑奋斗 */
/* 定期更新,敬请关注——开放,交流,深入,进步 */
/* Copyright fuhome.net 未来之家 实验室,让科技融入生活 */
/**********************************************************/

U8 DNS_Data[32]={0x12 ,0x34,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x03,0x77,0x77,0x77,0x06,0x66,0x75,0x68,0x6f,0x6d,0x65,0x03,0x6e,0x65,0x74,0x00,
0x00,0x01,0x00,0x01};
//获取 DNS: dev.fuhome.net

U8 DNS_OK=0;
U16 DNS_Time=0;
U8 DNS_Server[4]={0,0,0,0};

/*********************************/
/* 功能:1、发送查询包 */
/* 输入:无 */
/* 输出:无 */
/* 备注: */
/*********************************/

void DNS_Send_Request(void)
{

    memcpy(Tx_Buffer,DNS_Data,32);//将数据复制到缓冲区
    Write_SOCK_Data_Buffer(0, Tx_Buffer, 32);//端口0发送UDP数据包

}

www.longjiancn.com 龙剑奋斗博客

没有更多推荐了,返回首页