Giao tiếp giữa ESP8266 và C# thông qua TCP/IP

Hôm nay mình sẽ giới thiệu các bạn về giao tiếp giữa ESP8266 – đóng vai trò là Client và C# – đóng vai trò là server.

Nhiệm vụ của C# sẽ truyền dữ liệu về ESP8266, cụ thể là truyền tọa độ điểm ảnh mà ta vẽ trên winform về ESP8266 và hiển thị lên LCD OLED.

Phía server C#

1. Giao diện

Ta sẽ tạo một winform với các Control có nội dung sau:
– Form: đây là giao diện trống khi các bạn mới khởi tạo.
+ Event “Form1_Load”: sự kiện phát hiện khi bắt đầu mở chương trình.
+ Event “Form1_Closing”: sự kiện phát hiện khi bắt đầu đóng chương trình.
– Textbox: nhập địa chỉ IP của máy đóng vai trò là server.
+ Name: txt_ip
– Nút nhấn “Start Server”: khởi động server ứng với IP ở textbox txt_ip.
+ Name: bt_start
– Nút nhấn “Clear”: xóa nội dung đã vẽ trên khung PictureBox.
+ Name: bt_clear
– Danh sách ListBox: sẽ hiện thị địa chỉ IP của Client khi kết nối.
+ Name: list_client
– PictureBox: nơi ta dùng chuột để vẽ lên.
+ Name: pic_send
+ Size: 640,320 (mình sẽ giải thích sau nhé)
+ Event “pic_send_MouseDown”: dùng để phát hiện sự kiện khi ta nhấn chuột bắt đầu vẽ.
+ Event “pic_send_MouseMove”: dùng để lấy các tọa độ điểm khi di chuyển chuột trên khung vẽ, sau đó truyền về ESP8266.
+ Event “pic_send_MouseUp”: dùng để phát hiện sự kiện ta nhả chuột kết thúc nét vẽ.
Giao diện sau khi hoàn thành:

2. Code

Mình sử dụng phương thức giao tiếp là TCP/IP nên sẽ sử dụng thư viện SimpleTCP, các bạn có thể mở “Manage Nuget Packages…” để tải về nhé

Sau đó các bạn khai báo các thư viện cần dùng như sau:

using SimpleTCP;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

Các biến toàn cục mình khởi tạo:

bool start_send = false;
Bitmap temp_image;
SimpleTcpServer server;

Tại sự kiện “Form1_Load”, mình sẽ khởi tạo các sự kiện của “server” bao gồm:
– Khi Client kết nối.
– Khi Client ngắt kết nối.
– Mình sẽ không sử dụng sự kiện nhận dữ liệu “DataReceived” bởi vì trong bài hướng dẫn này chỉ truyền theo một phía từ C# xuống ESP8266.

    private void Form1_Load(object sender, EventArgs e)
    {
        server = new SimpleTcpServer();
        //server.Delimiter = 0x13;
        server.StringEncoder = Encoding.UTF8;
        server.DataReceived += Server_DataReceived;
        server.ClientConnected += server_ClientConnected;
        server.ClientDisconnected +=server_ClientDisconnected;
    }

Ứng với sự kiện Client kết nối, mình sẽ thêm địa chỉ IP của Client đó tại “list_client:

    private void server_ClientConnected(object sender, System.Net.Sockets.TcpClient e)
    {
        list_client.Invoke((MethodInvoker)delegate()
        {
            list_client.Items.Add(e.Client.RemoteEndPoint.ToString());
        });
    }

Ứng với sự kiện Client ngắt kết nối, mình sẽ remove địa chỉ IP của Client đó tại “list_client”:

    private void server_ClientDisconnected(object sender, System.Net.Sockets.TcpClient e)
    {
        list_client.Invoke((MethodInvoker)delegate()
        {
            list_client.Items.Remove(e.Client.RemoteEndPoint.ToString());
        });
    }

Khi nhấn nút “Start Server”, ta sẽ khởi tạo server ứng với IP của txt_IP và PORT 100:

    private void bt_start_Click(object sender, EventArgs e)
    {
        //System.Net.IPAddress ip = new System.Net.IPAddress("192.168.0.106");
        server.Start(System.Net.IPAddress.Parse(txt_ip.Text.ToString().Trim()), 100);
    }

Tại sự kiện “Form1_Closing”, mình sẽ đóng server đã khởi tạo

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        server.Stop();
    }

Tại sự kiện “pic_send_MouseDown”, mình sẽ set cho biến start_send là true, đồng thời gán hình ảnh hiện tại của pic_send vào một biến tạm thời là temp_image.

    private void pic_send_MouseDown(object sender, MouseEventArgs e)
    {
        start_send = true;
        temp_image = (Bitmap)pic_send.Image;       
    }

Tại sự kiện “pic_send_MouseMove”, tại đây:
– Ứng với vị trí chuột di chuyển theo phương X – e.Location.X, theo phương Y – e.Location.Y, mình sẽ vẽ trên biến tạm thời temp_image là nét vẽ màu xanh lá cây – green.
– Giá trị truyền về ESP8266 là X và Y ứng với giá trị tọa độ của chuột chia cho 5. Vì sao lại chia cho 5 mà không phải là con số nào khác, bởi vì: kích thước của pic_send là 640×320, kích thước của lcd oled là 128×64 nên mình sẽ xử lý số liệu ở trên máy tính trước rồi truyển về ESP8266 sau.
– Kiểu dữ liệu mình truyển về ESP8266 theo dạng: XXX:YYY\r. Ví dụ: 096:62\r (“\r” là xuống hàng nhé). Nếu có cách nào truyền dữ liệu hay hơn, hãy chỉ mình nhé.

    private void pic_send_MouseMove(object sender, MouseEventArgs e)
    {
        if (start_send == true)
        {
            if (e.Location.X < pic_send.Image.Width && e.Location.X > 0 && e.Location.Y >0 && e.Location.Y < pic_send.Image.Height)
            {
                temp_image.SetPixel(e.Location.X, e.Location.Y, Color.FromArgb(0, 255, 0));
                int X = e.Location.X / 5;
                int Y = e.Location.Y / 5;
                String location = X.ToString("000") + ":" + Y.ToString("000") + "\r";
                try
                {
                    server.Broadcast(location);
                }
                catch
                {

                }
                pic_send.Image = temp_image;
            }
        }
    }

Tại sự kiện “pic_send_MouseUp”, mình sẽ set cho biến start_send là false, để báo hiệu kết thúc nét vẽ.

    private void pic_send_MouseUp(object sender, MouseEventArgs e)
    {
        start_send = false;
    }

Khi nhấn nút “Clear”, mình sẽ gọi một hình ảnh trống có sẵn, đồng thời gửi tới ESP8266 lệnh “Clear\r” để xóa các điểm ảnh trên LCD.

    private void bt_clear_Click(object sender, EventArgs e)
    {
        server.Broadcast("Clear" + "\r");
        pic_send.ImageLocation = Application.StartupPath + "\\Image\\blank.png";
    }

Tiến hành chạy chương trình, nhập địa chỉ IP của máy bạn vào ô txt_IP, nhấn nút “Start Server”, sau đó thử vẽ xem nhé:

Phía Client ESP8266

1. Sơ đồ nối dây

Mình sử dụng LCD OLED 0.96 inch với độ phân giải 128×64, giao tiếp I2C.

2. Code

Khai báo các thư viện cần dùng:

#include "Wire.h"
#include "ESP8266WiFi.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"

Khai báo Port, địa chỉ IP server, cấu hình IP cho ESP8266, khởi tạo Client để kết nối server và kích thước màn hình LCD. – Cấu hình địa chỉ IP server theo IP của máy chứa chương trình C#.
– Cấu hình IP cho ESP8266 sao cho IP cùng lớp mạng với IP server.

#define OLED_RESET     -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define PORT 100
IPAddress server_ip(192, 168, 55, 110);
IPAddress local_IP(192, 168, 55, 120);
IPAddress gateway(192, 168, 55, 1);
IPAddress subnet(255, 255, 255, 0);
WiFiClient client;

Tại hàm setup, ta sẽ cho ESP kết nối vào Wifi cùng lớp mạng với máy tính hiện tại như sau (ssid và password các bạn tự điền nhé):

void setup() {
   Serial.begin(115200);
   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
   WiFi.begin("ssid", "password");
   WiFi.config(local_IP, gateway, subnet);
   while (WiFi.status() != WL_CONNECTED) {
     Serial.println("Connecting…");
     delay(100);
   }
   Serial.println("");
   Serial.print("Connected to ");
   Serial.print("IP address: ");
   Serial.println(WiFi.localIP());
 }

Tại hàm loop, mình sẽ thực hiện kết nối với server C#, sau khi kết nối được với server, sẽ chạy vào hàm while để nhận dữ liệu từ server gửi về.
Bởi vì dữ liệu từ server gửi về luôn có dạng: “xxx” + “\r” nên trong hàm while, mình sẽ viết “String line = client.readStringUntil(‘/r’);”. Nghĩa là đọc lần lượt từng giá trị điểm ảnh mà server gửi về, giá trị điểm ảnh này mình xử lý bằng cách cắt chuỗi theo định dạng cố định là giá trị X và Y. Vậy nên có thể ESP sẽ vẽ chậm hơn tay các bạn một xíu đấy :D.

void loop() {
  client.connect(server_ip, PORT);
  while (client.connected()) {
    String line = client.readStringUntil('\r');
    //mình sẽ viết code ở đây
    Serial.println(line);
    if (line != "" && line != "Clear")
    {
      int X = line.substring(0, 3).toInt();
      int Y = line.substring(4, 7).toInt();
      display.drawPixel(X, Y, SSD1306_WHITE);
      //Serial.print(X);
      //Serial.print("    ");
      //Serial.println(Y);
    }
    else if (line == "Clear")
    {
      display.clearDisplay();
    }
    display.display();
  }
}

Bây giờ tiến hành nạp code để xem thành quả nhé!

  • Khởi động chương trình C#, nhập địa chỉ IP máy tính hiện tại. Các bạn có thể tìm thấy địa chỉ IP thông qua tập lệnh cmd “ipconfig” – hiện tại IP máy mình sẽ là 192.168.55.107
  • Nhấn nút “Start Server”, lúc này, chương trình sẽ chờ phía Client – ESP8266 kết nối tới.
  • Cấu hình và nạp code cho ESP8266, mình sẽ chỉnh sửa code tại 3 dòng này để có thể kết nối được Wifi và giao tiếp được với server.
IPAddress server_ip(192, 168, 55, 107);
IPAddress local_IP(192, 168, 55, 120);
WiFi.begin("Huutam246", "khongcomatkhau");
  • Sau khi nạp code xong, ESP tiến hành kết nối Wifi và giao tiếp với server, tại giao diện C# sẽ hiện lên tên IP của ESP8266
  • Sau đó chúng ta tiến hành vẽ ở khung Paint nhé :D. Đây là thành phẩm của mình.

Kết luận

  1. Với dự án này, các bạn có thể giao tiếp từ ứng dụng C# với ESP8266 để thực hiện nhiệm vụ nào đó. Ví dụ: Truyền tọa độ điểm ảnh để ESP8266 điều khiển tay máy vẽ lại hình ảnh đó, có thể điều khiển xe từ xa, hoặc có thể in 3D thông qua Wifi chẳng hạn.
  2. Dự án này còn khuyết điểm là cần phải thay đổi IP cấu hình server cho ESP8266 và tên wifi, password truy cập, nếu các bạn muốn cách nhanh hơn, thì hãy comment để bài sau mình hướng dẫn dự án tương tự nhé!

 357 total views,  6 views today

Leave a Reply

Your email address will not be published. Required fields are marked *