小题大做之远程LED控制

2014-12-225.3万
精华项目
AI 快速预览详细 收起
本文介绍了如何通过远程LED控制项目来帮助孩子理解B/S架构的运作方式。项目使用了花生壳内网版商业服务来解决80端口被封的问题,实现了从外网访问家庭宽带中的装置。此外,还使用了Tomcat 8.0.15作为Web服务器,MySQL 5.6.12作为数据库,并通过NetBeans IDE 8.0.2进行开发。该项目不仅能够控制几个LED灯,还能收集温度数值,适合STEM教育场景,激发孩子的技术兴趣。
这个项目主要是为了给孩纸们透彻解释B/S的运作方式,涉及到的技术点比较杂,不过,项目仅仅控制几个LED灯、收集温度数值,故而称之为小题大做;P。

预备工作:

1. 工信部为了打击钓鱼网站,将家庭宽带的80端口都封掉了,且光猫没有固定IP地址。试图直接访问80端口的话,显示的是光猫的维护页面。为了实现从外网访问这个小小的装置,我向Oray购买了一个域名(www.openkevin.net)和花生壳内网版商业服务。
小题大做之远程LED控制图5

2. 花生壳内网版安装在一台老式的Lenovo E49上,开启内网映射。外网80端口直接穿透到内网192.168.1.5这台笔记本电脑的80端口上。如此这般,依赖花生壳动态解析,实现了只要能上网的地方,就能访问我们的装置。由于通过花生壳服务的转发,境外访问可能要比境内访问快。
小题大做之远程LED控制图1

3. Lenovo E49上安装了Tomcat 8.0.15作为Web服务器(Servlet/JSP container)。官方Ethernet Shield的兼容卡DFRobot Ethernet W5100将作为Web Client访问该服务器,进行后续的数据交换。
小题大做之远程LED控制图2

4. 后台数据库MySQL 5.6.12(配合图形界面Workbench 6.2CE),对孩纸们学习SQL很有帮助。真是羡慕如今的小孩纸,老夫想当年是生活在dBase III时代啊。
小题大做之远程LED控制图3

5. Servlet/JSP开发环境用了NetBeans IDE 8.0.2,估计要被Eclipse们嘲笑了,哈哈哈。老夫上手写Java时用的是Forte for Java,结果落下这个毛病。孩纸们以后多亲近亲近Eclipse,显得多么滴高大上呀。
小题大做之远程LED控制图4

创作许可协议

本项目采用 None(不开放任何权利,保留所有权利) 进行许可。

评论(23)
Primer
Primer
楼主主要是什么职业啊~~ 我想跟这楼主混混
kevinzhang19701
kevinzhang19701
作者
回复Primer
:L偶在保险公司做IT。从前端写Web页面一直做到后端数据库。越做越靠后,老板都不跟我打招呼了,你还跟我混。
Primer
Primer
楼主主要是什么职业啊~~ 我想跟这楼主混混
qq438078826469
qq438078826469
大神好长的文章
kevinzhang19701
kevinzhang19701
作者
回复qq438078826469
;P不长不长,还有不少细节没说透呢。
laofuz1024
laofuz1024
没有仔细看完文章,楼主辛苦了
看这结构,好像是把笔记本当成了一台Web服务器
UNO这里是获取传感器的数据,然后上传到服务器?服务器把数据存到数据库?
其它的客户端再从服务器上取得数据?
感觉你这是每隔一分钟就把传感器的数据都传到服务器吧
这样的项目,用JSP,这果真是小题大做了,用PHP+apache就简单多了!效率高一点吧
kevinzhang19701
kevinzhang19701
作者
回复laofuz1024
:):handshake
这个项目的原始目的,是给孩子们介绍B/S结构以及数据是如果传输的。加上了Arduino、LED、温度采集是为了增加一点乐趣,毕竟小孩子对枯燥的理论有一定的抗拒。使用Java而没有用PHP是让小孩子上手就站在J2EE的门槛上。
您理解得没错,笔记本电脑是Web服务器,充当数据中心。W5100板子会根据Arduino指令每隔1秒向Web服务器询问LED等开启状态,来调整4个LED的点亮/熄灭。每隔1分钟采集环境温度,上传Web服务器。后台MySQL数据库保存LED状态和温度。这种硬件与数据中心点分开的模式及富弹性,可以无限追加客户端硬件、采集的数据可以任意加工。绝对对得起小题大做的标题
以上供参考。:handshake
cj2q
cj2q
很有用 适合于很多的项目 与物联网
kevinzhang19701
kevinzhang19701
作者
回复cj2q
:D:handshake
Grey
Grey
这要膜拜一下,果断收藏
jssy
jssy
其实可以在Ethernet的基础上进一步封装为Restful Client,服务器端也改为Restful Service,这样就可以写成标准接口,再下去可能就是家控智能平台了。
哈哈:lol
kevinzhang19701
kevinzhang19701
作者
回复jssy
这个主意不错,RESTful web service更简单、更富有弹性,当然在安全性上,RESTful还需要借助第三方加密算法。
这个思路可以摸索出一套标准接口规范,以后将所有家庭传感器和其他硬件设备统一接入这个数据汇总点。进一步将数据汇总点做成嵌入式硬件,体积做成外置DVD大小。嗯,这个很有前途。
社区活动向导
社区活动向导
楼主码字辛苦啊,赞一个。。。
tyjjr
tyjjr
膜拜大神
FuKin
FuKin
用Ethernet W5100和W5200的区别是什么呢?
kevinzhang19701
kevinzhang19701
作者
回复FuKin
W5200尚未使用过,看介绍似乎比W5100更好。
与W5100相比,W5200具有以下几点优势:
支持高速SPI总线(最大100MHz),支持8个独立的端口同时连接。W5100只有4个独立端口。
提供休眠模式和网络唤醒,更低功耗。
极小巧的48pin QFN无铅封装,芯片体积更小。
使用同样非常简单。只需添加我们提供的库文件,覆盖Arduino IDE原有的W5100库文件,烧录一下就可以运行了。
W5200具备32KB内部通信缓冲,W5100只有16KB。
库文件也是新的,参考下列链接内容:
https://www.dfrobot.com.cn/image/data/DFR0272/Ethernet%20Shield%20For%20Arudino-%20W5200%20V1.0%20testing.zip
代码方面似乎没啥变化,5100时代的代码都可以移植过去。

kevinzhang19701
kevinzhang19701
作者
[font=微软雅黑]7. 后续扩展[/font]
这个小项目以上告一段落,但是只是刚开了个头,后续留了一些口子给孩纸们今年寒假进行扩充:
(a) 改进访客设置LED状态写入数据库流程,只有状态变化才写入,状态不变则不写。
(b) 温度采集添加日期与时间值,写入数据库(t2表追加两个字段)。
(c) 增加访客可以通过日期、时间段查询温度值,并可以排序(升序或者降序可选)。
(d) 查询结果温度值绘成曲线图。
[font=微软雅黑]8. 剧终[/font]
老夫小题大做,断断续续扯了两天,不知各位看官是否头晕恶心了。此处惊堂木一拍,落幕鞠躬。
欲知后事如何,且听下回分解。
[font=黑体](此处再补充一点点,本项目全部来自于开源软件的支持,在此鸣谢开源社区与所有应用到的开源项目)[/font]
kevinzhang19701
kevinzhang19701
作者
[font=微软雅黑]6. 代码部分[/font]
有了以上这些解释,想必孩纸们都能直接看代码了。老夫就不多费口舌,老夫领进门,运作靠个人,孩儿们操练起来。
6.1 首页Web page代码(mys.html):
[font=simsun]


Remotely controlled LEDs






















[/font]
效果图:

6.2 myservlet1.java:
[font=simsun]
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
@WebServlet(urlPatterns = {"/myservlet1"})
public class myservlet1 extends HttpServlet
{
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html;charset=UTF-8");
String p = request.getParameter("p");
String a4 = "0";
String a5 = "0";
String a6 = "0";
String a7 = "0";
String n = "0";
String a4s = "0";
String a5s = "0";
String a6s = "0";
String a7s = "0";
Connection conn = null;
Statement stmt = null;
ResultSet rs1 = null;
int s = 0;
try (PrintWriter out = response.getWriter())
{
if (p.equals("1"))
{
try
{
conn = DriverManager.getConnection("jdbc:mysql://localhost/mydata?" +
"user=root&password=15186okia");
stmt = conn.createStatement();
System.out.println("==");
System.out.println("查询最后一条记录...");
rs1 = stmt.executeQuery("SELECT * FROM t1 ORDER BY aid DESC LIMIT 1;");
while(rs1.next())
{
n = rs1.getString(2);
if (n.equals("1"))
{
a4s = "1";
}
else
{
a4s = "0";
}
n = rs1.getString(3);
if (n.equals("1"))
{
a5s = "1";
}
else
{
a5s = "0";
}
n = rs1.getString(4);
if (n.equals("1"))
{
a6s = "1";
}
else
{
a6s = "0";
}
n = rs1.getString(5);
if (n.equals("1"))
{
a7s = "1";
}
else
{
a7s = "0";
}
}
}
catch (SQLException ex)
{
System.out.println("Error in connection: " + ex.toString());
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
}
/* TODO output your page here. You may use following sample code. */
out.println("");
out.println("");
out.println("");
out.println("Remote Controlled LEDs.");
out.println("");
out.println("");
out.println("");
out.println("
");
out.println("
");
out.println("");
out.println("");
out.println("");
if (a4s.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
if (a5s.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
if (a6s.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
if (a7s.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
out.println("");
out.println("
A4:OffOnOffOn
A5:OffOnOffOn
A6:OffOnOffOn
A7:OffOnOffOn
");
out.println("
");
out.println("");
out.println("");
}
else
{
if (!request.getParameter("a4").equals("1"))
{
a4 = "0";
}
else
{
a4 = "1";
}
if (!request.getParameter("a5").equals("1"))
{
a5 = "0";
}
else
{
a5 = "1";
}
if (!request.getParameter("a6").equals("1"))
{
a6 = "0";
}
else
{
a6 = "1";
}
if (!request.getParameter("a7").equals("1"))
{
a7 = "0";
}
else
{
a7 = "1";
}
try
{
conn = DriverManager.getConnection("jdbc:mysql://localhost/mydata?" +
"user=root&password=15186okia");
stmt = conn.createStatement();
System.out.println("==");
System.out.println("追加记录......");
s = stmt.executeUpdate("INSERT INTO t1 (a4,a5,a6,a7) VALUES ('"
+ a4 + "','"
+ a5 + "','"
+ a6 + "','"
+ a7 + "')");
}
catch (SQLException ex)
{
System.out.println("Error in connection: " + ex.toString());
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
}
/* TODO output your page here. You may use following sample code. */
out.println("");
out.println("");
out.println("");
out.println("Remote Controlled LEDs.");
out.println("");
out.println("");
out.println("");
out.println("
");
out.println("
");
out.println("");
out.println("");
out.println("");
if (a4.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
if (a5.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
if (a6.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
if (a7.equals("0"))
{
out.println("");
out.println("");
}
else
{
out.println("");
out.println("");
}
out.println("");
out.println("");
out.println("");
out.println("");
out.println("
A4:OffOnOffOn
A5:OffOnOffOn
A6:OffOnOffOn
A7:OffOnOffOn
");
out.println("
");
out.println("");
out.println("");
}
}
}
//
/**
* Handles the HTTP GET method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP POST method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "Short description";
}//

}[/font]
6.3 myservlet2.java:
[font=simsun]import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
public class myservlet2 extends HttpServlet
{
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String a4 = "0";
String a5 = "0";
String a6 = "0";
String a7 = "0";
String n = "0";
response.setContentType("text/text");
try (PrintWriter out = response.getWriter())
{
Connection conn = null;
Statement stmt = null;
ResultSet rs1 = null;
int s = 0;
try
{
conn = DriverManager.getConnection("jdbc:mysql://localhost/mydata?" +
"user=root&password=15186okia");
stmt = conn.createStatement();
System.out.println("==");
System.out.println("查询最后一条记录...");
rs1 = stmt.executeQuery("SELECT * FROM t1 ORDER BY aid DESC LIMIT 1;");
while(rs1.next())
{
n = rs1.getString(2);
if (n.equals("1"))
{
a4 = "1";
}
else
{
a4 = "0";
}
n = rs1.getString(3);
if (n.equals("1"))
{
a5 = "1";
}
else
{
a5 = "0";
}
n = rs1.getString(4);
if (n.equals("1"))
{
a6 = "1";
}
else
{
a6 = "0";
}
n = rs1.getString(5);
if (n.equals("1"))
{
a7 = "1";
}
else
{
a7 = "0";
}
}
}
catch (SQLException ex)
{
System.out.println("Error in connection: " + ex.toString());
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
}
/* TODO output your page here. You may use following sample code. */
//out.println("");
//out.println("");
//out.println("");
//out.println("Servlet myservlet2");
//out.println("");
//out.println("");
//out.println("");
//out.println("
");
//out.println("UNO Var = " + uno + "
");
out.println("A4=" + a4 + ",A5=" + a5 + ",A6=" + a6 + ",A7=" + a7);
//out.println("");
//out.println("");
}
}
//
/**
* Handles the HTTP GET method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP POST method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "Short description";
}//

}[/font]
6.4 myservlet3.java:
[font=simsun]import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
public class myservlet3 extends HttpServlet
{
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter())
{
String unoTemp = request.getParameter("unoVar");
Connection conn = null;
Statement stmt = null;
ResultSet rs1 = null;
int s = 0;
try
{
conn = DriverManager.getConnection("jdbc:mysql://localhost/mydata?" +
"user=root&password=15186okia");
stmt = conn.createStatement();
System.out.println("==");
s = stmt.executeUpdate("INSERT INTO t2 (b1) VALUES (ROUND(" + unoTemp + ",2));");
System.out.println("温度写入完成...");
}
catch (SQLException ex)
{
System.out.println("Error in connection: " + ex.toString());
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
}
}
}
//
/**
* Handles the HTTP GET method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP POST method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "Short description";
}//

}[/font]
6.5 Project_LED_01.ino:
[font=simsun]#include
#include
#include
#include
#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server(192, 168, 1, 5);
IPAddress ip(192, 168, 1, 76);
EthernetClient client;
char c;
String result;
int myCount = 0;
float unoTemp = 0.00;
char unoTemps[6];
void setup()
{
Serial.begin(9600);
Ethernet.begin(mac, ip);
sensors.begin();
delay(1000);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
Serial.println("Ethernet connecting...");
}
void loop()
{
if (client.connect(server, 80))
{
myCount++;
if (myCount <= 60)
{
Serial.println("Connected");
client.println("POST http://192.168.1.5/servlet/myservlet2 HTTP/1.1");
client.println("Host:192.168.1.5");
client.println();
delay(1000);
while(client.available())
{
c = client.read();
result += c;
}
Serial.println("==");
Serial.println(result.substring(147,170));
if (result.substring(147,151).equals("A4=1"))
{
digitalWrite(4, HIGH);
}
else
{
digitalWrite(4, LOW);
}
if (result.substring(152,156).equals("A5=1"))
{
digitalWrite(5, HIGH);
}
else
{
digitalWrite(5, LOW);
}
if (result.substring(157,161).equals("A6=1"))
{
digitalWrite(6, HIGH);
}
else
{
digitalWrite(6, LOW);
}
if (result.substring(162,166).equals("A7=1"))
{
digitalWrite(7, HIGH);
}
else
{
digitalWrite(7, LOW);
}
}
else
{
sensors.requestTemperatures();
unoTemp = sensors.getTempCByIndex(0);
dtostrf(unoTemp, 3, 2, unoTemps);
String temps = (String)unoTemps;
Serial.println(temps);
delay(20);
Serial.println("Connected");
client.println("POST http://192.168.1.5/servlet/myservlet3?unoVar=" + temps + " HTTP/1.1");
client.println("Host:192.168.1.5");
client.println();
delay(500);
myCount = 0;
}
Serial.println();
client.stop();
result = "";
}
else
{
Serial.println("connection failed");
}
}[/font]
kevinzhang19701
kevinzhang19701
作者
5.5 SQL & MySQL
SQL使用query与服务器打交道,来提交查询或者获得结果,主要应用于关系型数据库系统,应用面较为广泛。SQL的S指structured,这种结构化查询语言是高级的、非过程化编程语言。允许用户在高层数据结构上工作,她不要求用户指定数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同数据库系统上可以使用相同的结构化查询语言作为数据输入与管理的接口。结构化查询语言语句还可以嵌套,这使她具有极大的灵活性和强大的功能。绝大多数的应用场合,完成一项任务仅需一条SQL命令即可,这也是其神奇的地方。
MySQL遵循ANSI SQL92标准且对其进行了扩展,这意味着ANSI SQL92的所有语法,MySQL都兼容可执行。
本项目中使用下列几条SQL命令,他们是----
(a) 查询数据库内LED最新状态:
[font=Verdana]
SELECT * FROM t1 ORDER BY aid DESC LIMIT 1;[/font]
(b) 将访客设置的LED状态写入数据库:
[font=Verdana] INSERT INTO t1 (A4, A5, A6, A7) VALUES (A4, A5, A6, A7);[/font]
(c) 将温度传感器值写入数据库:
INSERT INTO t2 (b1) VALUES (ROUND(unoTemp, 2));
注:表t1保存4个LED的开/关状态,字段分别是A4、A5、A6、A7(分别对应LED白在PIN 4、LED蓝在PIN 5、LED绿在PIN 6、LED红在PIN7)。状态开为1,状态关为0。
注:表t2保存温度值,只有一个字段b1,温度值保留2位小数。
注:本帖遵循保留字用大写,与引脚有关用大写,变量用小写。
5.6 Web page、HTML、CSS、JavaScript
有人问我为什么不直接让Ethernet Shield充当Web Server,这样还简单,反应速度也快。我是出于两点考虑:第一,这个项目是假期给孩纸们解释B/S运作模式用的,希望技术跨度和覆盖能大一点,便于他们未来步入J2EE领域;第二,Ethernet Shield充当Web Serve速度够快,但是在支持CSS/JavaScript上,尚有一定距离。我个人认为Ethernet shield就做好自己网卡的工作,把网页设计部分交给专业的去做,孩纸们爱怎么折腾就能怎么折腾。
顺便谈一下,我觉得未来IoT进入家庭或者企业的话,信息/数据还是会汇总到某个中心点,而不是分散到各个设备上。也就是说,人们访问的是某个总呈现页面(可能是Web形式的或者App形式的),而不是一个个设备上面独自的页面。这也是为什么Google买了Nest,小米想占据客厅Router/TV,这些都是信息的汇总点,谁能抢先做成行业标准,那么很快谁就会成为国际标准。
网页(Web Page)、HTML现在已经烂大街了,差不多有口气的,都会写两句。本来据说XML会淘汰掉这货,但是现在实际情况是XML首先没落了,被逼到了Web Service那块阵地。更甚的是,那块阵地又受到后起之秀JSON的围剿,除了保守派还在坚持,其他流派都被招安了。自HTML5推出后,HTML又一次起死回生,看来应验了那句,真理往往是简单的。作为21st世纪的孩纸,HTML就像乘法口诀那样需要掌握,未来B/S结构仍然是主流模式,为什么?很简单,把客户端这块成本节省掉,别老想打400电话来烦我。
CSS对页面和文字的渲染美化越做越强,也打破了很多原先的条条框框。当然更令人瞩目的是JavaScript,简直是跳跃式爆发。据2014年11月TIOBE排行榜,她已轻松排入前十位,且排名仍有上升余地。孩纸们如果想有个好饭碗,除了Java和C以外,JavaScript应该学习一下。学习JavaScript,推荐看一下犀牛书。


本项目涉及到的HTML和JavaScript是Form,当然啦,我们需要向Servlet提交点东西,总得麻烦From君。下面这行,看上去是不是很亲切:

我们会把这个放入项目的首页内。孩儿们一看便知呀,通过form来向myservlet1提交一个参数p的值是1,提交运用POST方式。
ganggang
ganggang
不错啊,我是java程序员,这个还没有开始玩
kevinzhang19701
kevinzhang19701
作者
回复ganggang
这个还是很好玩的;P
lauren
lauren
我勒个去的,一直想学用arduino开放自己控制的智能家居,就是不会用花生壳啊,云服务搭建啊什么的东西。果断收藏。。。不过貌似作为小白我还有很多在这个教程里面看不懂的。。。
比如NetBeans IDE 8.0.2这个java程序干嘛的。。。mySQL干嘛的。。。花生壳我知道,把家里ip转成公网的。。。但是想抽点时间试试!
楼主大神来头,貌似知识跨度很大。。。要服务器,要数据库搭建,还要搞硬件。。。
kevinzhang19701
kevinzhang19701
作者
回复lauren
:handshake:handshake
老夫觉得,搞硬件的,应该要搞点软件;同理,搞软件的也要搞点硬件。跨界不仅仅是赶时髦,其实能扩展思路与项目外延。;P
lauren
lauren回复kevinzhang19701
不会啊,真心觉得挺吊的,主要是知识域跨度很大,很给力
共3条回复,已加载2条,点击查看更多共3条回复,已加载全部
kevinzhang19701
kevinzhang19701
作者
[font=微软雅黑]5. 技术要点[/font]
5.1 花生壳解析
孩纸们都知道DNS的作用,简单滴说就是将域名字符串转换成IP地址。各级DNS都有配对表,自己找不到配对的,就向上级DNS询问,直到找到或者返回失败,最后更新自己的配对表(动态的或者静态的)。如果用Whois查询我购买的域名的话,就可以看到其实这个域名并不属于我,而是属于Oray公司,再由Oray公司租给我使用。DNS能够正确找到并翻译成IP地址,不过,这个IP是Oray的,而不是我光猫上的那个。
花生壳客户端安装在我的笔记本电脑上后,会产生一个指纹。客户端用这个指纹向其服务器汇报自己的位置信息(包含了光猫的动态IP地址、端口占用信息)。当外网访客访问我购买的域名的时候,DNS解析到Oray的IP地址,由服务器查到我光猫的位置,然后转发所有的数据包。那么孩纸们会问,80端口被封了,数据包怎么穿透呢?道理很简单,根本没用80端口。
花生壳客户端会随机试探高段端口号,找到一个未被光猫封掉的,就向服务器汇报这个端口号,让所有转发进来的数据包通过这个端口号进来。数据包返回,按照同样的路径。
如此这般,外网访客访问域名的时候(默认80端口),其实所有数据包是转发到我的光猫IP地址的某个大数字端口上。孩纸们,现在知道天朝网络慢的原因了吧。其实,这也是冰山一角,待以后我给你们解释我们伟大的放火长城的时候,再深入讨论(你没看错,是放火,天朝网民火很大)。
5.2 Tomcat & Java
WIKIPEDIA是这么定义的:Apache Tomcat (or simply Tomcat, formerly also Jakarta Tomcat) is an open source web server and servlet container developed by the Apache Software Foundation (ASF). Tomcat implements several Java EE specifications including Java Servlet, JavaServer Pages (JSP), Java EL, and WebSocket, and provides a "pure Java" HTTP web server environment for Java code to run in.
所以,我们选用Tomcat不仅仅因为她是一个性能不错的Web Server和Servlet container,还在于她遵循J2EE规范,是孩纸们未来步入J2EE的一个入口,而J2EE的跨平台特性是目前.Net无法比拟的。如果未来服务器访问频繁的话,可以在Tomcat前面装上Apache,让静态和动态内容分开处理。
在安装Tomcat前,需要先安装Java环境。同样道理,选用最新版本的Java SE 8u25。砖家们一直对Java技术褒贬不一,不过这些并不妨碍Java成为伟大的跨平台开发技术(微软的C#或多或少都在东施效颦)。物联网作为一个热词,最近一二年在孩纸们间流行,其实在20年前Sun基于Java的Jini技术就已经构想了万物相连的远景,只是Sun太过贪婪,此是后话,暂且按下不表。
Tomcat各版本的目录和文件结构都相同,这是向下兼容的好处。更新Tomcat后,只要把文件拷贝过来就行。注意下列几个目录与文件:
(a) conf\server.xml,这个是配置文件,里面重要的是定义了Web server的端口。Tomcat默认8080,本项目修改成80(其他端口也可以,不过尽量选用高段端口,避开常用的)。
(b) webapps\ROOT,这个目录是网页的老家。这里的index.html是默认情况下的首页文件。
(c) webapps\ROOT\WEB-INF,这个目录下面都是项目文件,重要的有web.xml、classes目录和lib目录。web.xml文件里面主要涉及各个servlet程序的配置。classes目录里面保存的是我们的3个servlet编译好的class文件。lib里面存放着的是MySQL的Connector/J的jar接口文件,没有她servlet就没法与MySQL数据沟通。
(d) bin\startup.sh和shutdown.sh(对应Windows系统下startup.bat和shutdown.bat)是启动和关闭Tomcat的两个shell文件。
5.3 Servlet/JSP
WIKIPEDIA如此说:Technically speaking, a "servlet" is a Java class in Java EE that conforms to the Java Servlet API, a standard for implementing Java classes which respond to requests. Servlets could in principle communicate over any client–server protocol, but they are most often used with the HTTP protocol. Thus "servlet" is often used as shorthand for "HTTP servlet". Thus, a software developer may use a servlet to add dynamic content to a web server using the Java platform. The generated content is commonly HTML, but may be other data such as XML. Servlets can maintain state in session variables across many server transactions by using HTTP cookies, or URL rewriting.
Architecturally, JSP may be viewed as a high-level abstraction of Java servlets. JSPs are translated into servlets at runtime; each JSP servlet is cached and re-used until the original JSP is modified.
上述解释了servlet与JSP的区别和关系,本项目没有使用JSP,是因为在显示这部分我们着力不多。无论是来自于外网的访客,还是来自于内网的W5100,他们都向Web server提交HTTP请求,而接管这些请求的是Tomcat。Tomcat将普通的HTML请求,直接返回给客户端,如果遇到servlet调用,那么直接将控制权交给servlet。这种客户端使用Browser(浏览器),服务器端Web Server的运作结构,被称为Browser/Server模式(简称B/S)。这种模式的好处是,我们对客户端的维护将降到极点(只要有浏览器就能用我们的项目),开发人员将更多滴关注服务器端,避免被客户端干扰,也减少了客户端维护的成本。当然一剑双刃,B/S模式严重依赖服务器的数据,且所有计算均在服务器端,服务器一旦挂掉,所有客户端均死翘翘了。
所有的servlet均是Java程序,本项目使用NetBeans这个IDE来开发。用IDE的好处,就像使用DFRobot I/O板一样,可以偷懒一点,让我们更多滴关注项目逻辑本身,快速切入主题,而不要被其他东西干扰。Servlet的介绍可以写一本超过1000页的教科书,这里不打算做过多讨论。幸运的是IDE里面有现成的servlet模板,我们将自己的逻辑代码嵌入即可。另一个高大上的IDE是Eclipse,正如她的名字那样是针对Sun的,哈哈哈,查查字典就知道了。


由于我们使用servlet去与数据库MySQL打交道,以完成数据的查询和写入的工作,因此孩纸们要快速学习一点SQL语言。
5.4 SQL
估计看到这里,孩纸们是不是感觉头晕了吧。要的就是这个恶心效果,哈哈哈,不要小看了一个小小的采集温度和点亮4个LED的项目。老夫一直是小题大做滴大湿啊,跟着老夫定然让你头晕目眩,自我感觉极端爆表。
SQL的定义: Structured Query Language is a special-purpose programming language designed for managing data held in a relational database management system(RDBMS), or for stream processing in a relational data stream management system(RDSMS).
kevinzhang19701
kevinzhang19701
作者
[font=微软雅黑]4. 逻辑部分[/font]

逻辑部分相对比较简单,如上图所示。
4.1 外网访客可以通过Internet访问到偶米滴信息中心点,感谢花生壳(后面“技术要点”里我会再稍微详细滴谈一谈)。不过,还得提醒孩纸们,运营商提供的光猫并不稳定,所以在外网访问时,有时候反应会比较慢。建议过个把星期,重启一下光猫和服务器。
4.2 充当数据交换中心点的笔记本电脑(Lenovo E49)配置并不高(听说已经停产),对于小型项目已戳戳鱿鱼了。老夫给公司同事做的几个讲座的内容也放在上面,顺便孩纸们也学习一哈吧。
4.3 充当Web服务器软件和Servlet容器的Tomcat,建议孩纸们一定要及时更新(有新版本出来,一定要安装新版本,所以大家要天天关注Apache网站哦)。为什么捏?自然出于稳定和安全的考虑。虽说连接到家庭网络上的设备数量不多,并且不那么昂贵或者机密,但是,作为一个通向外网的入口,保持一颗小人度君子的心态还是很有必要的。待今后接入这一网络的设备慢慢多起来,可以考虑用旧笔记本装一个防火墙。
4.4 如上节3.4描述的,Tomcat下存放了三个Servlet,分别接管三个方向的工作。Servlet1工作比较多,主要接受来自于外网访客对LED开/关状态的变化,并要将这些变化状态写入数据库(表t1)。Servlet2的工作相对单一一点,主要是回答W5100每隔1秒提交的LED状态查询。Servlet3的工作也比较单一,主要是接受W5100每隔60秒提交的温度数值,将其写入数据库(表t2),这样孩纸们可以在WorkBench里观察。
4.5 Arduino sketch只有一个,完成两个工作:
(a) 每隔1秒向服务器查询数据库里最新的LED状态,然后刷新LED高低电平。
(b) 每隔60秒向服务器提交[font=微软雅黑][font=宋体]DS18B20采集来的温度值。[/font][/font]
Mickey
Mickey
恩,光猫是不行了,我们这里光猫自己登不进去,不告诉密码
Mickey
Mickey
以前用EN28j60做过远程访问,没有用花生壳,就直接动态IP,只要不关路由器就不会变。
kevinzhang19701
kevinzhang19701
作者
回复Mickey
这个方法部分地区可能可以,不过像上海这里就不可行了。运营商不提供admin密码,这样就无法做port forward,另外光猫会被随机远程重启,并更换密码。
Angleo
Angleo
这个我喜欢~~
把一件很方便的事情,搞得很复杂~~:lol
LZ太有追求了~~
kevinzhang19701
kevinzhang19701
作者
回复Angleo
有困难要上,
没有困难,创造困难,也要上。:lol