第一章ESP8266的java软件仿真测试
第二章ESP8266硬件与软件测试
第三章ESP8266客户端与Java后台服务器联调
第四章ESP8266客户端与JavaWeb服务器联调
第五章ESP8266客户端与JavaWeb服务器网页联调
第六章ESP8266客户端与JavaWeb服务器网页实现数据的自动上传和下传联调
第七章ESP8266与JavaWeb服务器组成初级物联网自动监控系统
第八章ESP8266、STM32、Android和JavaWeb服务器组成中级物联网自动监控系统
第九章基于STM32C8T6、ESP8266-01S、JavaWeb、JSP、Html、JavaScript、Android、服务器和客户端设计、上位机和下位机设计等技术融合的物联网智能监控系统设计与实现
daodanjishui物联网核心原创技术之物联网监控系统的设计与实现,原创开源是我的风格,闭门造车是我的动力。
在上一章(第八章):ESP8266、STM32、Android和JavaWeb服务器组成中级物联网自动监控系统,该项目收到的反响不错,销量也不错,收益也不错,代码得到了广泛应用,也是自己感到欣慰的地方。该版本系统有一个缺陷就是不能控制点灯,只能进行客户端和服务器的监视作用,还有客户端和服务器报警功能,并没有实现真正意义上的“监控”。功能如下视频所示:
ESP8266、stm32、DHT11、Android、Javaweb和Html温湿度监控系统的设计
在这一章中(第九章),daodanjishui在上一章项目的基础上推出新的一款由Eclipse编写的开源App实现控制功能和监视功能,App没有报警功能,服务器有报警功能。如果有App报警需求请回到上一章。个人不想炒旧饭,展现的功能都是不断升级,监控系统的完善是靠功能逻辑的全方位设计和全栈编码技术的输出,加上编程界的“拿来主义”辅助,终于让这个系统升级到另一个境界。在原来的功能基础上,这次设计升级了功能,加入一个精美UI的物联网监控App 来实现监控功能,这个精美的App,代码精简,逻辑严谨,扩展能力强,读者如果阅读成功,就完全可以在我的基础上进行二次开发,代码虽然是“拿来主义”下的作品,但是加入了我自己的注释和原创代码部分,众所周知阅读别人的代码比自己写代码还困难,而我是读完别人的代码(客户端)再发力原创出新的(服务器+客户端)系统出来,难度都不是一个档次了。
在这一期项目的特色是:(1)保留原功能(2)在上一期的项目的基础上升级了服务器源码,让服务器支持手机app的查询请求和控制功能,完成物联网系统的全部功能。(3)手机app编程使用eclipse开发环境使用传统的java语言开发的app,代码精简,功能强大,界面清爽,最适合新手学习和提高。(4)下载链接赠送详细的入门教程4个,解析了代码的设计机制和编程思路。视频功能演示如下:
基于JavaWeb、Android的物联网智能监控系统
从上图可以看出汇集了:客户端STM32和ESP8266单片机和DHT11传感器、手机客户端、浏览器客户端和JavaWeb服务器组成了升级版全功能物联网自动监控系统。该系统运行流畅,具有稳定性和扩展性。延续了前几章内容,但是又在前几章内容和功能的基础上逐步深入构成了升级版的物联网监控系统。
系统框架图如下,逻辑构建是软件系统编程的核心,只用胸有成竹才能创作成功。
整个系统很庞大,涉及的技术太多了,后期可以在考虑CSDN开播或者开群讲述源码。后期考虑加入ESP32-CAM图传上去,也就是结合我ESP32-CAM物联网照相机的技术,代码已经初步写好了,不过这样系统就复杂很多了,现在在没有变复杂之前先分享这个电路方案再说吧。一个人构建整个智能物联网监控系统的逻辑是不简单的,必须拥有所需要的全栈技术。
整个系统相对于第八章的系统,变化和升级的地方就是增加一个手机客户端App的开发,同时也修改的原来服务器的代码,因为服务器要同时响应两种类型手机客户端、浏览器客户端和单片机客户端。
App源码是参照某宝的某丁科技购买Zigbee开发板赠送的客户端修改而成的。不过是daodanjishui的朋友购买的300多大洋Zigbee开发套件卖家赠送的源码,朋友叫daodanjishui教他如何二次开发这个App,可惜了我一看卖家只给了App的源码,服务根本不给任何源码,所以这个学习资料基本打水漂了,被坑了一地!下面请看某宝某丁的App截图:
App这个App原来是用Android studio来写的,我硬着头皮改为Eclipse的版本,再重头写一个Eclipse版本的JavaWeb服务器去匹配这个App。当时用Eclipse同时编写服务器和App程序在这个炎热的夏天整整写了三天代码,笔记本发热死机了4次,凭借的是我十年的嵌入式开发经验和五年Java开发技术加四年的Android开发经验积累的自信让我能坚信自己能胜任写出这套代码。当我点击App的开灯按钮成功点亮开发板上蓝色的灯的时候,我就知道自己闭门造车实现了物联网控制的核心技术,其实网上那些所谓的开源物联网系统很多都是代码缺失或者是代码过于复杂看不懂,一些读者下载了一大堆学习资料才发现开发更艰难,资料越多不见得是好事,沉下心编程才是王道。
为了让读者快速掌握这个系统,我裁剪了工程的代码,先分享部分功能的代码出来,让读者读懂和跑起来这个系统,后期如果想深入了解再去下载完整代码。
App客户端修改的代码适配手机(示例):
package com.example.mycontrol;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Created by daodanjihsui on 2022/8/25.
*/
public class HttpUtil {
private static final int TIMEOUT = 10000;// 10秒
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
String httpResult = ((String) msg.obj);
iHttpUtil.onSuccess( msg.what, httpResult );
}
};
protected IHttpUtil iHttpUtil;
public interface IHttpUtil{
void onSuccess( int type, String httpResult );
}
public void setHttpUtilInterface( IHttpUtil a ){
iHttpUtil = a; }
//拼接Http请求URL
private String getUrl( String ctrl ){
//String urlString="http://api.cd6969.com/api/gw/domb/?did="+SharedPreferencesUtil.getUserUid()+"&key="+SharedPreferencesUtil.getUserKey();
//这个是查询指令
String urlString="http://"+SharedPreferencesUtil.getUserUid()+":"+SharedPreferencesUtil.getUserKey()+"/daodanjishuiMonitor/Action?user=app"
+"&nocache="+new Date().getTime()
+"&ctrl=check";
if ( ctrl != null && !ctrl.equals("") ){
//urlString += "&ctrl=" + ctrl + "&t=2";
urlString="http://"+SharedPreferencesUtil.getUserUid()+":"+SharedPreferencesUtil.getUserKey()+"/daodanjishuiMonitor/Action?user=app"
+"&nocache="+new Date().getTime()
+"&ctrl="+ctrl;
System.out.println("手机发送的控制指令是:&ctrl="+ctrl);
}
return urlString;
}
public void getHttpData(final int type, final String ctrl ) {
final Runnable runnable = new Runnable() {
public void run() {
startThread( type, ctrl );
if ( type == Common.HTTP_GET_ENDDEVICE_DATA ){
//满足这个条件就是手机不断连接服务器的逻辑,跳出此条件不再循环
//当手机有其他操作的时候,getHttpData(type,ctrl)的type会发生改变,死循环将退出。
Log.i("ycc", "ycc:Thread===run99999==" + type + "###" + ctrl);
System.out.println("登陆信息" + type + "###" + ctrl);
handler.postDelayed(this, 4500);//4.5秒又重新执行一次这个线程了,对servlet小程序只能不断尝试去连接获取服务器回复,服务器不能主动回复。
//handler.postDelayed(this, 10000);//10秒又重新执行一次这个线程了,对servlet小程序只能不断尝试去连接获取服务器回复,服务器不能主动回复。
}
}
};
handler.postDelayed(runnable, 0);
}
private void startThread( final int type, final String ctrl ){
new Thread(new Runnable() {
@Override
public void run() {
String urlString = getUrl( ctrl );
String result = requestData( urlString );//第一种用get方法链接服务器的方法
Log.i("ycc", "ycc:Thread收到服务器的响应是:" + result);
Message msg = Message.obtain();
msg.what = type;
msg.obj = result;
handler.sendMessageDelayed(msg, 0);
}
}).start();
}
//耗时操作,必须放在线程中执行
private String requestData( String urlString ){
String result = "";
try {
URL url = new URL(urlString);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");// 设置请求的方式
urlConnection.setReadTimeout(10000);// 设置超时的时间
urlConnection.setConnectTimeout(10000);// 设置链接超时的时间
if (urlConnection.getResponseCode() == 200) {
InputStream is = urlConnection.getInputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
// 定义读取的长度
int len = 0;
// 定义缓冲区
byte buffer[] = new byte[1024];
// 按照缓冲区的大小,循环读取
while ((len = is.read(buffer)) != -1) {
// 根据读取的长度写入到os对象中
os.write(buffer, 0, len);
}
// 释放资源
is.close();
os.close();
// 返回字符串
result = new String(os.toByteArray());
System.out.println("获取服务器登录响应结束");
}else System.out.println("获取服务器登录响应失败");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 通过get方式提交参数给服务器
*/
public static String sendGetRequest(String urlPath,
Map<String, String> params, String encoding) throws Exception {
// 使用StringBuilder对象
StringBuilder sb = new StringBuilder(urlPath);
sb.append('?');
// 迭代Map
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(entry.getKey()).append('=')
.append(URLEncoder.encode(entry.getValue(), encoding))
.append('&');
}
sb.deleteCharAt(sb.length() - 1);
// 打开链接
URL url = new URL(sb.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "text/xml");
conn.setRequestProperty("Charset", encoding);
conn.setConnectTimeout(TIMEOUT);
// 如果请求响应码是200,则表示成功
if (conn.getResponseCode() == 200) {
// 获得服务器响应的数据
BufferedReader in = new BufferedReader(new InputStreamReader(
conn.getInputStream(), encoding));
// 数据
String retData = null;
String responseData = "";
while ((retData = in.readLine()) != null) {
responseData += retData;
}
in.close();
System.out.println("获取服务器登录响应结束");
return responseData;
}
System.out.println("获取服务器登录响应失败");
return "sendGetRequest error!";
}
}
在上面的代码可以看到收到的get请求的参数中存在“app”字符串的时候就进入手机客户端处理的逻辑。服务器会给手机客户端返回数据显示在手机app的界面上。
所以下面的是服务器设计的部分能运行的代码:
Action.java
package com.daodanjishui.iot;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Action
*/
@WebServlet("/Action")
public class Action extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Action() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String msg="";//回复客户端的信息
String SW_status="0";//灯的状态
// 解决返回中文乱码问题
response.setCharacterEncoding("utf-8");
String user = request.getParameter("user");
// 解决接收中文乱码问题
user = new String(user.getBytes("iso-8859-1"), "gb2312");
System.out.println("接收到的 用户名是:" + user);
String ctrl = request.getParameter("ctrl");
// 解决接收中文乱码问题
ctrl = new String(ctrl.getBytes("iso-8859-1"), "gb2312");
System.out.println("接收到的ctrl是:" + ctrl);
if(ctrl.equalsIgnoreCase("L1ON")){
//如果接收到的是开灯指令
System.out.println("你发送了开灯指令");
SW_status="1";
msg="1408800"+SW_status+"02976000029760001297600012976000"+"gateway_online";
}else if(ctrl.equalsIgnoreCase("L1OFF")){
//如果介绍到的是关灯指令
SW_status="0";
msg="1409900"+SW_status+"02976000029760001297600012976000"+"gateway_online";
System.out.println("你发送了关灯指令");
}else if(ctrl.equalsIgnoreCase("check")){
System.out.println("现在是心跳自动查询数据");
msg="1216600012976000029760000297600002976000"+"gateway_online";
}else{
System.out.println("你发送的控制指令格式不对");
msg="0297600002976000029760000297600002976000"+"gateway_offline";
}
response.getWriter().println(msg);//返回给客户端
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
STM32单片机和ESP826通过post请求上传环境参数的数据的代码:
if( Read_DHT11(&DHT11_Data)==SUCCESS)
{
printf("\r\n读取DHT11成功!\r\n\r\n湿度为%d.%d %RH ,温度为 %d.%d℃ \r\n",\
DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);
//printf("\r\n 湿度:%d,温度:%d \r\n" ,DHT11_Data.humi_int,DHT11_Data.temp_int);
//sprintf((char*)p,"\r\n读取DHT11成功!\r\n\r\n湿度为%d.%d %RH ,温度为 %d.%d℃ \r\n",\
DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);
//sprintf((char*)p,"hello,Temperature=%d.%d'C ,Humidity=%d.%d RH\r\n",DHT11_Data.temp_int,DHT11_Data.temp_deci,DHT11_Data.humi_int,DHT11_Data.humi_deci);//返回温湿度给服务器
//sprintf((char*)p,"GET http://192.168.253.1:8080/test_TCP/ChatServlet?action=getMessages&signname=%d.%d %RH HTTP/1.1\r\nHost:192.168.253.1:8080\r\nConnection: keep-alive\r\n\r\n",DHT11_Data.humi_int,DHT11_Data.humi_deci);//测试数据
//sprintf((char*)p,"GET http://192.168.253.1:8080/test_TCP/ChatServlet?action=getMessages&signname=%d HTTP/1.1\r\nHost:192.168.253.1:8080\r\nConnection: keep-alive\r\n\r\n",DHT11_Data.humi_int);//测试数据
sprintf((char*)p,"POST http://192.168.1.9:8080/test_TCP/ChatServlet?action=sendMessage&user=Robot1&speak=Humidity:%d.%dRH---Temperature:%d.%d'C----LED:%d HTTP/1.1\r\nHost:192.168.1.9:8080\r\nConnection: keep-alive\r\n\r\n",DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci,LED_DATA_IN);//测试数据
//sprintf((char*)p,"GET http://192.168.253.1:8080/test_TCP/ChatServlet?signname=%d HTTP/1.1\r\nHost:192.168.253.1:8080\r\nConnection: keep-alive\r\n\r\n",DHT11_Data.humi_int);//测试数据
//sprintf((char*)p,"POST http://192.168.253.1:8080/test_TCP/ChatServlet?action=sendMessage&user=Robot1&speak=Humidity:%d.%dRH---Temperature:%d.%d'C HTTP/1.1\r\nHost:192.168.253.1:8080\r\nConnection: keep-alive\r\n\r\n",DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);//测试数据
}
else
{
printf("Read DHT11 ERROR!\r\n");
sprintf((char*)p,"hello,T=0 ,H=0\r\n");//返回温湿度给服务器
}
(1)技术说明:整个系统的设计设计到多种核心技术包括了:ESP8266AT指令使用、STM32库函数与ESP8266透传配网技术、JavaWeb原生态开发技术、服务器开发技术、浏览器客户端开发技术、安卓客户端开发技术、js脚本开发技术、UI开发技术、单片机与浏览器表单提交和接收技术等等。
(2)补充说明:传感器我只用到了DHT11采集温湿度,开关只控制一盏灯。传输和控制协议参考了某丁科技的逻辑,有些读者问为什么不用Zigbee来写?其实我就是嫌弃这个Zigbee CC2530单片机运行慢、不开源、价格贵。后期出一期Zigbee物联网开发的专题也是没有问题的。
(3) 逻辑说明:其实整个系统就是客户端与服务器的交互,首先单片机作为一个客户端与服务器链接,保持一个心跳的联系,服务器不断查询单片机的状态,当然也可以返回控制指令给单片机;然后是
“1.升级设计说明”所描述的服务器与手机客户端的通信,服务器接收手机客户端的两种类型的请求,第一种请求手机是查询环境参数的请求,第二种请求是手机发送控制开关灯的控制请求;最后就是所有的模块功能全部整合在一起。
查询逻辑的设计
(1)单片机客户端发送采集的环境数据给服务器。
(2)服务器存入单片机上传的环境数据。
(3)手机客户端发送查询请求给服务器。
(4)服务器返回单片机上传的数据给手机App
(5)手机App将接收的环境数据显示在UI界面上
如图下图所示:
控制逻辑的设计
(1)手机客户端发送控制的数据给服务器。
(2)服务器存入客户端上传的控制指令准备发送给单片机客户端。
(3)服务器等待单片机客户端主动发送环境参数请求顺便把手机控制指令返回给单片机客户端。
(4)单片机客户端接收到控制指令之后改变自己的IO口状态控制点灯或者关灯,再返回自己的IO口状态给服务器。
(5)服务器接收单片机上传的数据再将这些数据转发给手机客户端
(6)手机客户端收到这些数据之后更新UI,将开关灯的状态画出来。
如图下图所示:
注意:手机接收数据和更新UI逻辑设计还是很精妙的,感兴趣的读者可以下载源码看看。系统经过长时间测试和验证,人品担保能正常运行。
这部分的仿真和调试有两个版本:
第一个版本是测试基本通信的,免费开源
第二个版本是全部功能,收费开源,也就是视频演示的效果。
两个版本随君选择。
第一个版本调试与仿真
(1)先运行Eclipse编写的javaee服务器。
(2)在上图的输入框输入测试手机app上传指令的格式,查看返回的消息。红色字体就是服务器返回的信息,打印信息就是客户端发送的消息。如下图所示(app和L1ON是控制LED1开灯的指令仿真测试):
那么关灯的如下所示(红色字体是服务器返回给客户端的,客户端会根据这些数据解析在UI界面画出所有的信息):
如果你乱输入数据,会发现设备就挂了。如下图所示:
(2)打开app,输入服务器的IP地址和端口号(注意手机和服务器必须在一个局域网)
(3)点击开灯按钮看看,app和服务器都用什么反应,呵呵这个阉割版的功能很好玩的,因为不需要硬件,单单软件操作 服务器和客户端都有反应,数据都是写死的。让新手很快上手,老手很快知道如何进行二次开发了。
下面是手机app发送心跳数据自动查询服务器的截图
手机app的截图如下,看到环境参数和在线数据都产生了变化:
下面是触发点灯按钮之后服务器的打印(看到服务器打印L1OFF):
下面是开灯之后的app响应截图(发现开灯了并且环境参数和在线擦参数都发送了变化,非常有意思的):
第二个版本的仿真与调试详情看看视频了。功能更强悍。写了好几天的,读源码都花了几周了。
服务器调试截图:
单片机源码截图:
串口调试助手打印单片机调试信息:
通信协议:
根据两种仿真调试的结果知道满足博文提出的需求。
展望:后期将会推出物联网图像监控系统和图像处理,MQTT图传和MQTT远程控制,daodanjishui品牌值得信赖,精彩值得期待!
第一个版本全部源码附上代码传送链接:https://download.csdn.net/download/niruxi0401/21495690?spm=1001.2014.3001.5503
直接跳转到下载处
第二个版本全部源码附上代码传送链接:https://www.cirmall.com/circuit/26550/
直接跳转到下载处