#include
#include
#include
const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
const char* server_host = "192.168.1.100"; // Python Socket服务器所在主机的IP地址
const int server_port = 12345; // Python Socket服务器监听的端口号
WiFiClient client;
String response;
void setup() {
Serial.begin(9600);
delay(1000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to WiFi");
}
void loop() {
if (!client.connected()) {
Serial.print("Connecting to ");
Serial.print(server_host);
Serial.print(":");
Serial.println(server_port);
if (client.connect(server_host, server_port)) {
Serial.println("Connected to server");
} else {
Serial.println("Connection failed");
return;
}
}
float temp = 24.5; // 假设读取到的温度为24.5°C
float humid = 50.0; // 假设读取到的湿度为50%
StaticJsonDocument<200> doc;
doc["temp"] = temp;
doc["humid"] = humid;
doc["led"] = false; // 默认关闭LED
serializeJson(doc, response);
Serial.print("Sending message: ");
Serial.println(response);
client.print(response);
delay(5000); // 每隔5秒钟发送一次数据
if (client.available()) {
String message = client.readStringUntil('\n');
Serial.print("Received message: ");
Serial.println(message);
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, message);
if (!error) {
bool led_state = doc["led"];
Serial.print("LED state: ");
Serial.println(led_state);
// 根据收到的LED状态控制LED
// do something...
} else {
Serial.println("Failed to parse message");
}
}
}
实现了连接Wi-Fi网络、创建Socket客户端并向Python Socket服务器发送温湿度和LED状态信息。在发送数据前,我们使用ArduinoJson库构造JSON格式的数据,并将其存储在response
字符串变量中。之后,我们通过client.print()
方法将这个字符串发送给Python Socket服务器。
同时,我们还使用了client.available()
方法判断Python Socket服务器是否有回复消息。如果有,我们使用client.readStringUntil('\n')
方法读取这个消息并解析其中的LED状态。需要注意的是,在解析收到的JSON消息时,我们仍然使用了ArduinoJson库。
import tkinter as tk
from ttkbootstrap import Style
import socket
import json
# 定义全局变量存储温度、湿度和LED状态的信息
temp = 0.0
humid = 0.0
led_state = False
def create_ui():
# 创建主窗口
root = tk.Tk()
root.title("Socket服务器")
root.geometry('600x400')
style = Style(theme='flatly')
# 创建Frame容器
frame_top = tk.Frame(root)
frame_top.pack(pady=20)
frame_bottom = tk.Frame(root)
frame_bottom.pack(pady=10)
# 添加Label组件
label_temp = tk.Label(frame_top, text="温度", font=("Helvetica", 18))
label_temp.pack(side=tk.LEFT, padx=5)
label_humid = tk.Label(frame_top, text="湿度", font=("Helvetica", 18))
label_humid.pack(side=tk.LEFT, padx=5)
# 添加仪表盘组件
from tkinter import Canvas
temp_val = tk.StringVar()
temp_val.set(f"{temp} ℃")
humid_val = tk.StringVar()
humid_val.set(f"{humid} %")
canvas_temp = Canvas(frame_top, width=100, height=100, bg='white')
canvas_temp.pack(side=tk.LEFT, padx=20)
canvas_humid = Canvas(frame_top, width=100, height=100, bg='white')
canvas_humid.pack(side=tk.LEFT, padx=20)
def update_gauge():
# 更新温度仪表盘
temp_val.set(f"{temp} ℃")
canvas_temp.delete("all")
canvas_temp.create_arc(10, 10, 90, 90, start=45, extent=270, style=tk.ARC, outline='red', width=2)
canvas_temp.create_arc(10, 10, 90, 90, start=45, extent=270 * temp / 50, style=tk.ARC, outline='green', width=5)
canvas_temp.create_text(50, 50, text=temp_val.get(), font=("Helvetica", 18))
# 更新湿度仪表盘
humid_val.set(f"{humid} %")
canvas_humid.delete("all")
canvas_humid.create_arc(10, 10, 90, 90, start=45, extent=270, style=tk.ARC, outline='blue', width=2)
canvas_humid.create_arc(10, 10, 90, 90, start=45, extent=270 * humid / 100, style=tk.ARC, outline='green',
width=5)
canvas_humid.create_text(50, 50, text=humid_val.get(), font=("Helvetica", 18))
# 定时更新仪表盘
root.after(1000, update_gauge)
# 初始更新仪表盘
update_gauge()
# 添加按钮组件
from tkinter import ttk
button_text = tk.StringVar()
button_text.set("开灯" if not led_state else "关灯")
def click_button():
global led_state
led_state = not led_state
socket_send({"led": led_state, "temp": temp, "humid": humid})
button_text.set("关灯" if led_state else "开灯")
button = ttk.Button(frame_bottom, textvariable=button_text, command=click_button)
button.pack(side=tk.LEFT, padx=10)
return root
def socket_server():
# 创建Socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定IP地址和端口号
host = '127.0.0.1'
port = 12345
s.bind((host, port))
# 监听连接
s.listen(5)
print("等待连接...")
while True:
# 接受客户端连接请求
c, addr = s.accept()
print("连接地址:", addr)
while True:
try:
# 接收消息
data = c.recv(1024).decode()
# 解析JSON格式的消息并更新全局变量
msg = json.loads(data)
global temp, humid, led_state
temp = msg["temp"]
humid = msg["humid"]
led_state = msg["led"]
# 发送响应消息
response_msg = json.dumps({"led": led_state}).encode()
c.send(response_msg)
except Exception as e:
print(f"接收异常: {e}")
break
# 关闭客户端连接
c.close()
def socket_send(msg):
# 创建Socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
host = '127.0.0.1'
port = 12345
s.connect((host, port))
print('发送消息:', msg)
try:
# 发送消息
sent_bytes = s.send(json.dumps(msg).encode())
while sent_bytes < len(json.dumps(msg)):
sent_bytes += s.send(json.dumps(msg[sent_bytes:]).encode())
# 接收响应消息
response_data = s.recv(1024).decode()
response_json = json.loads(response_data)
global led_state
led_state = response_json['led']
except Exception as e:
print(f"发送异常: {e}")
finally:
# 关闭连接
s.close()
if __name__ == '__main__':
# 创建GUI界面
root = create_ui()
# 启动Socket服务器
import threading
t_server = threading.Thread(target=socket_server)
t_server.start()
# 进入主循环
root.mainloop()
socket_server()
函数添加了一个无限循环,等待客户端持续不断地发送消息给服务器。接收到消息之后,服务器会立即回复一个包含LED状态信息的响应消息。这样可以保证客户端和服务器间始终保持连接状态。
模拟测试数据
import socket
import json
import random
import time
def socket_send(msg):
# 创建Socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
host = '127.0.0.1'
port = 12345
s.connect((host, port))
try:
# 发送消息
sent_bytes = s.send(json.dumps(msg).encode())
while sent_bytes < len(json.dumps(msg)):
sent_bytes += s.send(json.dumps(msg[sent_bytes:]).encode())
# 接收响应消息
response_data = s.recv(1024).decode()
response_json = json.loads(response_data)
# 打印 LED 状态
led_state = response_json.get('led', None)
if led_state is not None:
led_status = '关闭' if not led_state else '打开'
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}][LED] {led_status}")
except Exception as e:
print(f"发送异常: {e}")
finally:
# 关闭连接
s.close()
while True:
# 模拟不断生成温度、湿度数据和 LED 数据
temp = round(random.uniform(18, 28), 1)
humid = round(random.uniform(30, 80), 1)
led = bool(random.getrandbits(1))
data = {"temp": temp, "humid": humid, "led": led}
# 调用socket_send()将数据发送给服务器
socket_send(data)
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}][发送成功] 温度={temp}℃,湿度={humid}%,LED={'打开' if led else '关闭'}")
# 等待1秒钟后再次发送数据
time.sleep(1)
MFC
// MyServerDlg.cpp : implementation file
//
#include "stdafx.h"
#include "MyServer.h"
#include "MyServerDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CMyServerDlg dialog
CMyServerDlg::CMyServerDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MYSERVER_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_bLedOn = FALSE;
}
void CMyServerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TEMP_EDIT, m_tempEdit);
DDX_Control(pDX, IDC_HUMID_EDIT, m_humidEdit);
}
BEGIN_MESSAGE_MAP(CMyServerDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_LED_BTN, &CMyServerDlg::OnBnClickedLedBtn)
END_MESSAGE_MAP()
// CMyServerDlg message handlers
BOOL CMyServerDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_server.Create(8080, SOCK_STREAM);
m_server.Listen();
return TRUE; // return TRUE unless you set the focus to a control
}
void CMyServerDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMyServerDlg::OnQueryDragIcon()
{
return static_cast(m_hIcon);
}
void CMyServerDlg::OnBnClickedLedBtn()
{
// TODO: Add your control notification handler code here
m_bLedOn = !m_bLedOn;
CString strLed;
if (m_bLedOn)
{
strLed = _T("关灯");
}
else
{
strLed = _T("开灯");
}
GetDlgItem(IDC_LED_BTN)->SetWindowText(strLed);
// send message to ESP32
CString strMsg;
strMsg.Format(_T("{\"led\":%d}"), m_bLedOn);
SendToClient(strMsg);
}
void CMyServerDlg::SendToClient(CString strMsg)
{
for (int i = 0; i < m_clients.GetCount(); i++)
{
SOCKET sock = m_clients.GetAt(i);
int len = strMsg.GetLength();
char* buf = new char[len + 1];
memset(buf, 0, len + 1);
WideCharToMultiByte(CP_ACP, 0, strMsg, -1, buf, len + 1, NULL, NULL);
send(sock, buf, len, 0);
delete[] buf;
}
}
void CMyServerDlg::OnAccept()
{
SOCKET sock = m_server.Accept();
m_clients.Add(sock);
AfxBeginThread(ClientThread, (LPVOID)sock);
}
UINT CMyServerDlg::ClientThread(LPVOID lpParam)
{
SOCKET sock = (SOCKET)lpParam;
char buf[1024];
int len;
CString strMsg;
while ((len = recv(sock, buf, 1024, 0)) > 0)
{
buf[len] = 0;
strMsg += CString(buf);
int pos = strMsg.Find('\n');
while (pos >= 0)
{
CString strJson = strMsg.Left(pos);
strMsg = strMsg.Mid(pos + 1);
Json::Reader reader;
Json::Value root;
if (reader.parse(strJson.GetBuffer(0), root))
{
if (root.isMember("temp"))
{
double temp = root["temp"].asDouble();
CString strTemp;
strTemp.Format(_T("%.1f ℃"), temp);
AfxGetMainWnd()->SendMessage(WM_UPDATE_TEMP, (WPARAM)&strTemp, 0);
}
if (root.isMember("humid"))
{
double humid = root["humid"].asDouble();
CString strHumid;
strHumid.Format(_T("%.1f %%"), humid);
AfxGetMainWnd()->SendMessage(WM_UPDATE_HUMID, (WPARAM)&strHumid, 0);
}
}
pos = strMsg.Find('\n');
}
}
closesocket(sock);
((CMyServerDlg*)AfxGetMainWnd())->m_clients.Remove(sock);
return 0;
}
LRESULT CMyServerDlg::OnUpdateTemp(WPARAM wParam, LPARAM lParam)
{
CString* pStr = (CString*)wParam;
m_tempEdit.SetWindowText(*pStr);
delete pStr;
return 0;
}
LRESULT CMyServerDlg::OnUpdateHumid(WPARAM wParam, LPARAM lParam)
{
CString* pStr = (CString*)wParam;
m_humidEdit.SetWindowText(*pStr);
delete pStr;
return 0;
}
其中,CMyServerDlg
是我们的主窗口类,它继承自CDialogEx
。在OnInitDialog
中,我们创建了一个socket服务器,并监听端口8080。在OnAccept
中,我们接受了一个客户端连接,并创建了一个新的线程ClientThread
来处理这个客户端的消息。
在ClientThread
中,我们使用了JsonCpp
库来解析从客户端发送过来的json消息,并根据消息内容更新界面上的温度和湿度。
在OnBnClickedLedBtn
中,我们通过SendToClient
函数向客户端发送了一个消息,来控制LED的开关。在SendToClient
函数中,我们遍历了所有连接的客户端,并向它们发送了消息。
在OnUpdateTemp
和OnUpdateHumid
中,我们分别更新了界面上的温度和湿度文本框的内容。
需要注意的是,我们在ClientThread
中使用了SendMessage
函数来更新界面,而不是直接操作界面上的控件。这是因为MFC是单线程模型,不能在非主线程中直接操作界面,否则会导致程序崩溃。因此,我们需要使用SendMessage
函数来将更新界面的任务转移到主线程中执行。
基于ESP32的Arduino代码的示例,实现了将DHT11传感器采集到的温度和湿度数据发送到MFC创建的socket服务器,并接收服务器发送过来的LED控制指令:
#include
#include
#include
#include
#define DHTPIN 4
#define DHTTYPE DHT11
const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
const char* host = "192.168.1.100"; // MFC服务器的IP地址
const int port = 8080; // MFC服务器监听的端口号
DHT dht(DHTPIN, DHTTYPE);
WiFiClient client;
void setup()
{
Serial.begin(9600);
delay(1000);
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
dht.begin();
}
void loop()
{
float temp = dht.readTemperature();
float humid = dht.readHumidity();
if (isnan(temp) || isnan(humid)) {
Serial.println("Failed to read from DHT sensor!");
delay(1000);
return;
}
// send data to server
StaticJsonDocument<64> doc;
doc["temp"] = temp;
doc["humid"] = humid;
String strJson;
serializeJson(doc, strJson);
strJson += "\n"; // 消息以换行符结束
if (!client.connected()) {
if (!connectToServer()) {
return;
}
}
client.print(strJson);
// receive data from server
while (client.available()) {
String line = client.readStringUntil('\n');
StaticJsonDocument<32> doc;
DeserializationError error = deserializeJson(doc, line);
if (error) {
Serial.println("Failed to parse JSON!");
return;
}
if (doc.containsKey("led")) {
bool led = doc["led"];
digitalWrite(LED_BUILTIN, led ? LOW : HIGH);
// send response to server
StaticJsonDocument<16> doc;
doc["led"] = led;
String strJson;
serializeJson(doc, strJson);
strJson += "\n";
client.print(strJson);
}
}
delay(1000);
}
bool connectToServer()
{
Serial.print("Connecting to server...");
if (client.connect(host, port)) {
Serial.println("connected!");
return true;
}
else {
Serial.println("connection failed!");
return false;
}
}