Arduino-ESP32通过socket和python或MFC进行通信,点灯和温湿度

#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函数中,我们遍历了所有连接的客户端,并向它们发送了消息。

OnUpdateTempOnUpdateHumid中,我们分别更新了界面上的温度和湿度文本框的内容。

需要注意的是,我们在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;
  }
}

你可能感兴趣的:(python,开发语言,mfc)