前言:
這是大四上學期做的物聯網實驗,總共4個實驗,這是第4個,也是最難的一個,當然大部分程式是我的老師撰寫的,我只是跟著實驗指導書做出來的,希望可以幫到大家,
1. 系統作業原理
溫濕度感知節點(Client),實時采集溫濕度資料,然后通過WiFi模塊,以TCP協議
將采集資料實時無線傳輸給服務端(Server);服務端將接收到的溫濕度資料存于資料庫
中;用戶終端(User)從服務端獲取實時采集和歷史資料,用以監測、分析、查詢等應
用,
2. 系統 設計
用戶應用程式界面如圖所示,

服務器界面:

使用串口助手看資料:
如果不寫服務器程式和用戶應用程式,也可以使用串口助手來觀察資料
操作方法如下:
- 打開串口除錯工具SSCOM,如下圖所示,
- 實驗以開發板作為客戶端(Client),以電腦作為服務器端(Server),所以,在SSCOM中,埠號選擇TCPServer,服務器IP(本地IP)、埠設定與main函式中的一致,
- 按下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)
{
//濕度資料款框
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302301.html
標籤:其他
