物联网实验-温湿度实时监测系统

前言:

这是大四上学期做的物联网实验,总共4个实验,这是第4个,也是最难的一个,当然大部分程序是我的老师编写的,我只是跟着实验指导书做出来的,希望可以帮到大家。

1. 系统工作原理

温湿度感知节点(Client),实时采集温湿度数据,然后通过WiFi模块,以TCP协议
将采集数据实时无线传输给服务端(Server);服务端将接收到的温湿度数据存于数据库
中;用户终端(User)从服务端获取实时采集和历史数据,用以监测、分析、查询等应
用。

2. 系统 设计

用户应用程序界面如图所示。
在这里插入图片描述

服务器界面:
在这里插入图片描述

使用串口助手看数据:
如果不写服务器程序和用户应用程序,也可以使用串口助手来观察数据

操作方法如下:

  1. 打开串口调试工具SSCOM,如下图所示。
  2. 实验以开发板作为客户端(Client),以电脑作为服务器端(Server)。所以,在SSCOM中,端口号选择TCPServer,服务器IP(本地IP)、端口设置与main函数中的一致。
  3. 按下SSCOM“侦听”按钮,若系统显示“已连接”,表明开发板已经建立了与服务器端的TCP连接,则可以看到温湿度的实时监测数据,。
    在这里插入图片描述

终端设备:
主要用到的元器件有:
STM32F103C8T6、ESP8266、DHT11、DS18B20、ST-LINK下载器、方口数据线

在这里插入图片描述

3. 程序编写

STM32部分源码:
这部分代码太多了,只给出主函数的代码。

#include "filelib.h"

int main(void)
{
	char *sendtxt = (char*)malloc(48);

	strcpy(P_data.wifi_ssid,"XIAOCHUN");			//设置连接WiFi名称
	strcpy(P_data.wifi_psd,"3118003167");			//设置连接WiFi密码
	strcpy(P_data.wifi_ip,"192.168.43.217");		//设置连接服务器IP
	strcpy(P_data.wifi_port,"12345");				//设置连接服务器端口
	ESP8266_Init ();   								//初始化ESP8266
	DHT11_GPIO_Config();
	DS18B20_Init();
	
	macESP8266_CH_ENABLE();							//使能ESP8266
	ESP8266_AT_Test();								//检测ESP8266的AT是否启动(若10次测试失败,则结束程序运行)
	ESP8266_Net_Mode_Choose(STA); 					//设置wifi模块为STA模式
	
	//连接wifi
	while (!ESP8266_JoinAP(P_data.wifi_ssid, P_data.wifi_psd)) 
{
	//连接wifi失败,重连
}	
	
	ESP8266_Enable_MultipleId(DISABLE);				//关闭WiFi多连接
	
//连接到远程服务器
	while (!ESP8266_Link_Server(enumTCP, P_data.wifi_ip, P_data.wifi_port, Single_ID_0)) 
{
	//连接服务器失败,重连

}
	
	while (!ESP8266_UnvarnishSend()) ;						//开启穿透模式

	while(1)
	{
		Delay_ms(1500);										//每隔1500ms获取一次温湿度
		//Dev_data.temp = readtemp();        				//获取温度
		Dev_data.temp =DS18B20_GetTemperture();
		sprintf(sendtxt,"%s%.2f%s","温度:",Dev_data.temp,"℃");
		ESP8266_SendString ( ENABLE,sendtxt,0, Single_ID_0 );	//发送温度值到服务器

		Dev_data.humi = Get_Humi_Value();                		//获取湿度
		sprintf(sendtxt,"%s%u%s","湿度:",Dev_data.humi,"%RH");
		ESP8266_SendString ( ENABLE,sendtxt,0, Single_ID_0 );	//发送温度值到服务器
//		ESP8266_SendString ( ENABLE, "Welcome!\r\n", 0, Single_ID_0 );	//发送湿度值到服务器
	}
}

服务器源码:

#define _CRT_SECURE_NO_WARNINGS

#if defined(_WIN32) || defined(_WIN64)  //为了支持windows平台上的编译
#include <windows.h>
#endif

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<winsock.h>
#include "mysql.h"  //该文件在…\mysql-8.0.18-winx64\include下

#pragma comment(lib,"ws2_32.lib")

using namespace std;
void initialization();
int main() {
	//以下是MySQL数据库操作程序 
	MYSQL mysql, * sock; //声明MySQL的句柄 
	const char* host = "localhost"; //数据库服务器IP 
	const char* user = "root"; //连接MySQL的用户名 
	const char* passwd = "123456"; //连接MySQL的用户密码
	const char* db = "iotdatabase"; //连接数据库的名字
	unsigned int port = 3306; //这是MySQL的服务器的端口,如果没有修改过的话就是3306
	const char* unix_socket = NULL; //unix_socket这是unix下的,在Windows下把它设置为NULL 
	unsigned long client_flag = 0; //这个参数一般为0 
	char query_str[200]; //MySQL命令字符串  
	mysql_init(&mysql); //连接数据库之前必须使用初始化函数进行初始化 

	//连接MySQL  
	if ((sock = mysql_real_connect(&mysql, host, user, passwd, db, port, NULL,0 )) == NULL)  //unix_socket  client_flag
	{
		cout << "连接mysql失败" << endl;
		exit(1); //exit(0);正常的关闭所有程序; exit(1):有错误的关闭所有程序
	}

	//以下是Socket通信服务端程序 
	int recv_len = 0; //定义长度变量 
	int len = 0;

	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	SOCKET s_accept;

	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	SOCKADDR_IN accept_addr;
	//initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;			//协议簇类型:TCP/IP–IPv4
	server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.43.217");		//服务器IP 192.168.43.217
	server_addr.sin_port = htons(12345);			//服务器端口

	//初始化套接字 
	WORD w_req = MAKEWORD(2, 2); //版本号 
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata); //初始化套接字
	if (err != 0) {
		WSACleanup(); //中止套接字 
	}

	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);	//SOCK_STREAM—TCP流
								 //SOCK_DGRAM—UDP数据报;SOCK_RAW—原始套接字 
	if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		WSACleanup();
		cout << "失败" << endl;
	}
	else {
		cout << "套接字绑定成功!" << endl;
		cout << "IP:192.168.43.217" << endl;
		cout << "端口:12345" << endl;
	}

	//设置套接字为监听状态
	if (listen(s_server, SOMAXCONN) < 0) {
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else {
		cout << "设置监听状态成功!" << endl;
	}
	cout << "服务端正在监听连接,请稍候...." << endl;

	//接受连接请求
	len = sizeof(SOCKADDR);
	s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len);
	if (s_accept == SOCKET_ERROR) {
		cout << "连接失败!" << endl;
		WSACleanup();	//释放DLL资源
		return 0;
	}
	cout << "连接建立,准备接受数据" << endl;

	//接收数据
	while (1) {
		char recv_buf[100] = { "\0" };	//定义接受缓冲区,并赋初值Null
		recv_len = recv(s_accept, recv_buf, 100, 0);		//接收数据长度
		if (recv_len < 0) {
			cout << "接受失败!" << endl;
			break;
		}
		else {
			//获取温湿度数据 数据格式:温度数据 + 湿度数据 #
			char* p1 = NULL;
			char* p2 = NULL;
			char* p3 = NULL;
			//分割字符串安全函数,这个函数将剩余的字符串存储在buf变量中,而不是静态变量中,从而保证了安全性。
			//char *strtok_s( char *strToken, const char *strDelimit, char **buf);
			string tempData = strtok_s(recv_buf, "+", &p2);
			string humiData = strtok_s(p2, "#", &p3);
			
			//获取采集时间
			char str[50];
			SYSTEMTIME st;       //系统时间变量
			GetLocalTime(&st);  //获得系统时间
			sprintf(str, "%u-%u-%u %u:%u:%u",st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
			string datetimeDA = str;   	//将采集时间转换为字符串 

			//拼接MYSQL查询命令字符串
			string sql = "insert into acquisitiondata(tempData,humiData,datetimeDA) values ('" + tempData + "','" + humiData + "','" + datetimeDA + "')";
			strcpy_s(query_str, sql.c_str()); 
			//将采集的温湿度插入MySQL数据表acquisitiondata
			mysql_real_query(&mysql, query_str, strlen(query_str));  //执行由query_str指向的SQL查询
			cout << "将采集的温湿度插入MySQL数据表acquisitiondata" << endl;
		}
	}

	//关闭服务端套接字
	closesocket(s_server);
	closesocket(s_accept);

	//释放DLL资源
	WSACleanup();
	return 0;
}

void initialization() {
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字库失败!" << endl;
	}
	else {
		cout << "初始化套接字库成功!" << endl;
	}
	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字库版本正确!" << endl;
	}
}

用户程序源码:

using System.Windows;
using MySql.Data.MySqlClient;
using System.Data;
using System.Linq;
using System.Threading;

namespace UserApp
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();   //窗体界面定义初始化
            string connectionString = "server=localhost;port=3306;database=iotdatabase;charset=utf8;user id=root;password=123456;pooling=false;";//MySQL数据库连接字符串
            MySqlConnection conn = new MySqlConnection(connectionString); //连接MySQL数据库
            conn.Open();                     //打开连接
            DataTable dt = new DataTable(); //定义数据表
            string sqlstring = "SELECT * from acquisitiondata where numRecord = (SELECT max(numRecord)FROM acquisitiondata)"; //查询实时数据命令字符串
            MySqlCommand cmd = new MySqlCommand //定义MySQLCommand对象
            {
                Connection = conn,
                CommandText = sqlstring,
                CommandType = CommandType.Text
            };
            SynchronizationContext _syncContext = SynchronizationContext.Current;
            Thread LogThread = new Thread(new ThreadStart(DoService)); //定义线程
            LogThread.IsBackground = true; //设置线程为后台线程
            LogThread.Start(); //开启线程

            void DoService()
            {
                while (true)
                {
                    MySqlDataAdapter da = new MySqlDataAdapter(cmd); //获取实时数据
                    da.Fill(dt); //将数据填充到数据表dt

                    DataRow dr_last = dt.AsEnumerable().Last();
                    _syncContext.Post(SetTextBox1, dr_last[1].ToString()); //通过线程更新txBoxTemp
                    _syncContext.Post(SetTextBox2, dr_last[2].ToString()); //通过线程更新txBoxHumi
                    _syncContext.Post(SetTextBox3, dr_last[3].ToString()); //通过线程更新txBoxTime
                    Thread.Sleep(1000);
                }
            }
            void SetTextBox1(object text)
            {
                txBoxTemp.Text = text.ToString(); //更新txBoxTemp
            }
            void SetTextBox2(object text)
            {
                txBoxHumi.Text = text.ToString(); //更新txBoxHumi
            }
            void SetTextBox3(object text)
            {
                txBoxTime.Text = text.ToString(); //更新txBoxTime
            }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //查询按钮
            string connectionString = "server=localhost;port=3306;database=iotdatabase;charset=utf8;user id = root; password = 123456; pooling = false; ";
            MySqlConnection conn = new MySqlConnection(connectionString);  //连接MySQL数据库
            conn.Open();                                                  //打开连接
            DataTable dt = new DataTable();                                //定义数据表
            string sqlstring = "SELECT * FROM acquisitiondata;";          //查询所有记录命令字符串
            MySqlCommand cmd = new MySqlCommand();                        //定义MySQLCommand对象
            cmd.Connection = conn;
            cmd.CommandText = sqlstring;
            cmd.CommandType = CommandType.Text;
            MySqlDataAdapter da = new MySqlDataAdapter(cmd);
            da.Fill(dt);                                                   //将数据填充到数据表dt
            dataGridViewTH.ItemsSource = dt.DefaultView;                 //dataGridViewTH绑定数据表dt
            conn.Close();                                                //关闭数据库连接
        }
        private void BtnClear_Click_1(object sender, RoutedEventArgs e)
        {
            //清空按钮
            string connectionString = "server=localhost;port=3306;database=iotdatabase;charset=utf8;user id = root; password = 123456; pooling = false; ";
            MySqlConnection conn = new MySqlConnection(connectionString);  //连接MySQL数据库
            conn.Open();                                                  //打开连接
            DataTable dt = new DataTable();                                //定义数据表                                                         
            string sqlstring = "TRUNCATE acquisitiondata;"; //TRUNCATE TABLE 删除表中的所有行,而不记录单个行删除操作
            MySqlCommand cmd = new MySqlCommand();                        //定义MySQLCommand对象
            cmd.Connection = conn;
            cmd.CommandText = sqlstring;
            cmd.CommandType = CommandType.Text;
            MySqlDataAdapter da = new MySqlDataAdapter(cmd);
            da.Fill(dt);                                                   //将数据填充到数据表dt
            dataGridViewTH.ItemsSource = dt.DefaultView;                 //dataGridViewTH绑定数据表dt
            conn.Close();                                                //关闭数据库连接
        }
        private void txBoxTime_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            //采集时间框
        }
        private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            //温度数据框
        }
        private void txBoxHumi_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            //湿度数据款框
        }
    }
}

最后:
需要资料的可以自行下载。下载链接
下载操作:
在这里插入图片描述

  • 8
    点赞
  • 113
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

⁽⁽ଘ晴空万里ଓ⁾⁾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值