Nghiên cứu về tín hiệu hồng ngoại của Remote điều hòa, ứng dụng trong các dự án nhà thông minh <Phần 2>

I. Làm thế nào để nhận tín hiệu raw dài hơn 255?

 Nếu các bạn chưa hiểu bài viết này mình đang nói về vấn đề gì thì có thể các bạn nên đọc qua bài viết Nghiên cứu tín hiệu điều khiển điều hòa, ứng dụng trong các dự án nhà thông minh Phần I của mình nhé

Như đã nói ở trên, hầu hết các thư viện hiện tại chỉ đang hỗ trợ các bạn giải mã tín hiệu hồng ngoại với kích thước bộ nhớ đệm file raw (thô) là 100. Bằng việc cấu hình lại thư viện Irremote như mình đã hướng dẫn trong phần 1, các bạn có thể tăng tối đa lên 255. Nhưng điều đó chỉ đáp ứng được 50% số điều khiển hiện có. Thực tế 50% còn lại là các loại điều khiển điều hòa. Chúng có kích thước tín hiệu rất lớn. Để giải quyết được vấn đề đó, các bạn sử dụng đoạn code sau:

// Mặc định led thu hồng ngoại được kết nối tới pin 2 của arduino. Tuy nhiên đối với YUN và LEONARDO các bạn cần kết nối tới pin 3 nhé

#define maxLen 800

volatile  unsigned int irBuffer[maxLen];

volatile unsigned int x = 0;

void setup() {

  Serial.begin(115200); //chú ý BAUD là 115200

  attachInterrupt(1, rxIR_Interrupt_Handler, CHANGE);

}


void loop() {

  Serial.println(F("Nhan 1 nut tren dieu khien – nhan 1 lan"));

  delay(5000);

  if (x) { //nếu có tín hiệu hồng ngoại

    Serial.println();

    Serial.print(F("Raw: ("));

    Serial.print((x - 1));

    Serial.print(F(") "));

    detachInterrupt(0);

    for (int i = 1; i < x; i++) {

      if (!(i & 0x1)) Serial.print(F("-"));

      Serial.print(irBuffer[i] - irBuffer[i - 2]);

      Serial.print(F(", "));

    }

    x = 0;

    Serial.println();

    Serial.println();

    attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);

  }

}

void rxIR_Interrupt_Handler() {

  if (x > maxLen) return;

  irBuffer[x++] = micros();

}

Sau khi nạp code xong, sử dụng 1 điều khiển bất kỳ để test. Ở đây mình dùng điều khiển điều hòa Mitsubitshi và mình nhận được ở Serial Monitor tín hiệu như sau :

 

Raw: (584) 18712, -3392, 1728, -384, 1288, -380, 1288, -380, 464, -368, 468, -364, 468, -364, 1288, -384, 464, -368, 464, -368, 1288, -380, 1288, -380, 468, -368, 1284, -384, 464, -368, 464, -368, 1288, -380, 1292, -380, 464, -368, 1288, -380, 1292, -376, 464, -368, 468, -364, 1292, -380, 464, -368, 464, -368, 1296, -372, 464, -368, 464, -368, 468, -364, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -364, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -364, 468, -368, 1284, -384, 1288, -380, 464, -368, 468, -364, 468, -368, 1288, -380, 464, -368, 1292, -376, 1288, -380, 468, -364, 468, -364, 468, -368, 464, -368, 464, -368, 1288, -380, 1292, -376, 468, -364, 1292, -380, 1288, -380, 468, -364, 468, -368, 1284, -384, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 1288, -380, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -368, 464, -368, 1288, -380, 464, -368, 468, -364, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 1292, -380, 464, -368, 464, -368, 464, -368, 1288, -380, 468, -368, 1288, -380, 1288, -380, 12940, -3392, 1728, -384, 1292, -376, 1292, -376, 468, -368, 464, -368, 464, -420, 1236, -380, 464, -368, 468, -364, 1288, -384, 1292, -376, 464, -368, 1288, -380, 468, -364, 468, -368, 1284, -384, 1288, -380, 464, -368, 1288, -380, 1288, -384, 464, -368, 464, -368, 1288, -380, 464, -368, 464, -368, 1292, -376, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -424, 412, -364, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -372, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 1288, -380, 1288, -384, 464, -368, 464, -368, 464, -368, 1288, -380, 464, -368, 1288, -380, 1292, -380, 464, -368, 464, -368, 464, -368, 468, -364, 468, -364, 1292, -380, 1288, -380, 464, -368, 1288, -380, 1288, -384, 464, -368, 464, -368, 1288, -380, 464, -368, 464, -368, 468, -364, 468, -364, 468, -368, 1284, -384, 464, -368, 464, -368, 464, -368, 468, -364, 468, -364, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 464, -368, 468, -368, 464, -416, 416, -368, 464, -368, 464, -368, 464, -424, 408, -424, 412, -364, 468, -368, 464, -368, 464, -420, 412, -368, 464, -368, 464, -368, 468, -364, 468, -368, 464, -368, 464, -420, 412, -424, 408, -424, 1232, -436, 412, -420, 412, -420, 412, -368, 464, -368, 464, -420, 412, -368, 464, -368, 464, -368, 468, -420, 412, -368, 464, -368, 464, -420, 412, -420, 416, -420, 408, -368, 468, -420, 412, -420, 412, -420, 1236, -432, 412, -420, 416, -420, 412, -368, 1284, -436, 412, -420, 1236, -432, 1236, -380,

 

Trong đoạn mã trên, 2 phần tử đầu tiên là Raw: (584) trong đó Raw là mã thô, 584 là số phần tử. Thực tế các phần tử trong mảng này là thời gian mà led phát hồng ngoại nhấp nháy. Việc cần làm bây giờ là các bạn hãy loại bỏ các dấu “-” bằng cách sử dụng công cụ find & replace trong 1 trình soạn thảo văn bản bất kì. Sau đó đưa chúng vào mảng như sau:

unsigned int raw[584]={ 3392, 1728, 384, 1288, 380, 1288, 380, 464, 368, 468, 364, 468, 364, 1288, 384, 464, 368, 464, 368, 1288, 380, 1288, 380, 468, 368, 1284, 384, 464, 368, 464, 368, 1288, 380, 1292, 380, 464, 368, 1288, 380, 1292, 376, 464, 368, 468, 364, 1292, 380, 464, 368, 464, 368, 1296, 372, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 1284, 384, 1288, 380, 464, 368, 468, 364, 468, 368, 1288, 380, 464, 368, 1292, 376, 1288, 380, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 1288, 380, 1292, 376, 468, 364, 1292, 380, 1288, 380, 468, 364, 468, 368, 1284, 384, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 1288, 380, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 1288, 380, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 1292, 380, 464, 368, 464, 368, 464, 368, 1288, 380, 468, 368, 1288, 380, 1288, 380, 12940, 3392, 1728, 384, 1292, 376, 1292, 376, 468, 368, 464, 368, 464, 420, 1236, 380, 464, 368, 468, 364, 1288, 384, 1292, 376, 464, 368, 1288, 380, 468, 364, 468, 368, 1284, 384, 1288, 380, 464, 368, 1288, 380, 1288, 384, 464, 368, 464, 368, 1288, 380, 464, 368, 464, 368, 1292, 376, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 424, 412, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 372, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 1288, 380, 1288, 384, 464, 368, 464, 368, 464, 368, 1288, 380, 464, 368, 1288, 380, 1292, 380, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 1292, 380, 1288, 380, 464, 368, 1288, 380, 1288, 384, 464, 368, 464, 368, 1288, 380, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 1284, 384, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 368, 464, 416, 416, 368, 464, 368, 464, 368, 464, 424, 408, 424, 412, 364, 468, 368, 464, 368, 464, 420, 412, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 420, 412, 424, 408, 424, 1232, 436, 412, 420, 412, 420, 412, 368, 464, 368, 464, 420, 412, 368, 464, 368, 464, 368, 468, 420, 412, 368, 464, 368, 464, 420, 412, 420, 416, 420, 408, 368, 468, 420, 412, 420, 412, 420, 1236, 432, 412, 420, 416, 420, 412, 368, 1284, 436, 412, 420, 1236, 432, 1236, 380, };

 

Như vậy các bạn đã có được mã raw để tiến hành gửi. Và cách gửi thì các bạn thực hiện như sau

 

#include <IRremote.h>

IRsend irsend;

unsigned int raw[584]={ 3392, 1728, 384, 1288, 380, 1288, 380, 464, 368, 468, 364, 468, 364, 1288, 384, 464, 368, 464, 368, 1288, 380, 1288, 380, 468, 368, 1284, 384, 464, 368, 464, 368, 1288, 380, 1292, 380, 464, 368, 1288, 380, 1292, 376, 464, 368, 468, 364, 1292, 380, 464, 368, 464, 368, 1296, 372, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 1284, 384, 1288, 380, 464, 368, 468, 364, 468, 368, 1288, 380, 464, 368, 1292, 376, 1288, 380, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 1288, 380, 1292, 376, 468, 364, 1292, 380, 1288, 380, 468, 364, 468, 368, 1284, 384, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 1288, 380, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 1288, 380, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 1292, 380, 464, 368, 464, 368, 464, 368, 1288, 380, 468, 368, 1288, 380, 1288, 380, 12940, 3392, 1728, 384, 1292, 376, 1292, 376, 468, 368, 464, 368, 464, 420, 1236, 380, 464, 368, 468, 364, 1288, 384, 1292, 376, 464, 368, 1288, 380, 468, 364, 468, 368, 1284, 384, 1288, 380, 464, 368, 1288, 380, 1288, 384, 464, 368, 464, 368, 1288, 380, 464, 368, 464, 368, 1292, 376, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 424, 412, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 372, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 1288, 380, 1288, 384, 464, 368, 464, 368, 464, 368, 1288, 380, 464, 368, 1288, 380, 1292, 380, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 1292, 380, 1288, 380, 464, 368, 1288, 380, 1288, 384, 464, 368, 464, 368, 1288, 380, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 1284, 384, 464, 368, 464, 368, 464, 368, 468, 364, 468, 364, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 464, 368, 468, 368, 464, 416, 416, 368, 464, 368, 464, 368, 464, 424, 408, 424, 412, 364, 468, 368, 464, 368, 464, 420, 412, 368, 464, 368, 464, 368, 468, 364, 468, 368, 464, 368, 464, 420, 412, 424, 408, 424, 1232, 436, 412, 420, 412, 420, 412, 368, 464, 368, 464, 420, 412, 368, 464, 368, 464, 368, 468, 420, 412, 368, 464, 368, 464, 420, 412, 420, 416, 420, 408, 368, 468, 420, 412, 420, 412, 420, 1236, 432, 412, 420, 416, 420, 412, 368, 1284, 436, 412, 420, 1236, 432, 1236, 380, };

void setup()

{

  Serial.begin(9600);

}

void loop() {

irsend.sendRaw(raw,584,38);

delay(4000);

}

 

Trong đó 584 là số phẩn tử, 38 là tần số gửi tính bằng khz

 

Tuy nhiên nhiều bạn nói rằng: Tôi đã làm như vậy nhưng vẫn không thể gửi được tín hiệu hồng ngoại bằng mã raw, dù là tôi sử dụng điều khiển khác loại với điều khiển điều hòa.

Mình xin gửi tới các bạn 9 vẫn đề thường mắc phải khi làm việc với hồng ngoại.

II. 9 vấn đề thường gặp phải khi làm việc với hồng ngoại

1. Sử dụng một thư viện

Hiện tại có 2 thư viện được viết cho aruduino về hồng ngoại

  • IrRemote là bản gốc và có rất nhiều ví dụ trực tuyến, tuy nhiên mình khuyên các bạn nên sử dụng thư viện IRremote chưa được cải tiến nếu bạn cần dịch mã raw
  • IRLib là một bản hiện đại hơn viết lại của IrRemote, với một số cải tiến. Đây là thư viện tốt nhất nếu các bạn muốn giải mã các tín hiệu điều khiển thông dụng

2. Đọc datasheet của linh kiện trước khi làm việc – đây là điều quan trọng đối với tất cả các dự án điện tử

  • Các bạn cần nghiên cứu và xác định chính xác chân tín hiệu của led thu hồng ngoại bằng cách nghiên cứu datasheet của chúng
  • Thậm chí ngay cả trong cùng 1 hãng sản xuất, thứ tự các chân kết nối cũng khác nhau. Ví dụ như đối với led thu của trung quốc có tên VS1838B có thứ tự chân giống như đối với led thu của hãng Vishay có tên TOSP 34438. Nhưng thực tế ngay trong hãng Vishay này thứ tự chân của các dòng led cũng khác nhau như Đối với TSOP348.., TSOP344..: 1 = OUT, 2 = GND, 3 = VS Đối với TSOP322.., TSOP324..: 1 = OUT, 2 = VS, 3 = GND. Chính điều này đã khiến mình làm cháy trên dưới chục con led thu hồng ngoại.

Hãy chắc chắn rằng bạn biết đâu là chân âm, đâu là chân dương của led phát hồng ngoại

3. Sử dụng một IR thu chất lượng tốt

  • Một sai lầm phổ biến là sử dụng IR receiver rẻ nhất và có sẵn trong các thiết bị như đầu thu của Trung quốc
  • Hãy chọn led thu tốt nếu các dự án của bạn cần đến độ chính xác, hoặc có thể tháo trong các đầu đài, tivi cũ hỏng có nguồn gốc tốt (không phải của Trung quốc)

4.Tránh kết nối trực tiếp led phát với chân của Arduino

  • Các bạn có thể tính được trị số điện trở bằng định luật ôm để kết nối vào chân led tránh hiện tượng cháy bóng
  • Bạn có thể xác nhận các led phát còn hoạt đồng bằng cách sử dụng một máy ảnh kỹ thuật số trên điện thoại, tablet hoặc webcam của bạn quay lại led phát, nếu thấy có ánh sáng màu tím phát ra là ok

5. Hãy làm việc độc lập từng thành phần

  • Hãy để bộ thu phát làm việc thành công trước khi bạn kết nối nó với các thiết bị phức tạp khác có thể ảnh hưởng tới hệ thống
  • Cả hai thư viện mình dẫn ở trên, đều đi kèm với một tập hợp các ví dụ mẫu. Hãy làm việc với các ví dụ và các bạn sẽ hiểu được rất nhiều về tín hiệu hồng ngoại.
  • Tất nhiên, nếu bạn là 1 chuyên gia thì hãy đi thẳng vào vấn đề phức tạp mà không cần ngó qua ví dụ đơn giản nữa

6. Thử nghiệm ở gần trước

  • 1 số led thu phát chỉ hoạt động được ở cự ly gần hoặc có thể việc kết nối của bạn có vấn đề, vì thế hãy thử nghiệm ở cự ly gần trước rồi mới đi ra xa để kiểm tra cự ly tối đa mà 2 led có thể giao tiếp được với nhau

7. Tái cấu hình thư viện của bạn

  • Như đã hướng dẫn ở phần I, việc cấu hình lại thư viện là điều quan trọng nếu bạn có nhu cầu muốn giải mã tín hiệu điều khiển có kích thước lớn.
  • Điều hoà nhiệt độ thường gửi đầy đủ các cấu hình của nó mỗi khi một phím trên remote được nhấn. Điều này dẫn đến tín hiệu dài hơn nhiều so với TV.
  • Tín hiệu của điều hòa không khí có thể lên tới 128 bit hoặc hơn, so với tín hiệu thông thường là 32bit của TV
  • Xác định lại giá trị RAWBUF trong thư viện IrRemote, hoặc sử dụng đoạn code mà mình giới thiệu trong phần 1.

8. Tránh xung đột với các thư viện khác

  • IrRemote & IRLib đều sử dụng chân kết nối với led thu hồng ngoại nhất định, kể cả timer. Đối với Uno là chân 3, timer1. Đối với MEGA là chân 9 và timer2. Vì vậy khi sử dụng kết hợp với các thư viện khác hãy chú ý việc bố trí chân kết nối và timer nhé!

9. Nhiễu - Đừng quên sự tồn tại của bức xạ hồng ngoại mặt trời!

  • Mặt trời là một nguồn sáng rất mạnh. Ánh sáng mặt trời có thể chiếu đến trong thông qua cửa sổ có thể gây nhiễu các tín hiệu hồng ngoại của bạn.
  • Thậm chí các thiết bị khác như đèn LED hay Plasma TV, đèn halogen hay đèn led ngay trên boad arduino của bạn cũng có thể ảnh hưởng tới tín hiệu bạn nhận được
  • Cách đơn giản nhất là cô lập từng nguồn nhiễu cho đến khi bạn có thể xác định được đâu là thứ ảnh hưởng tới tín hiệu và sau đó loại bỏ nguồn nhiễu đó

Tôi đã kiểm tra các vấn đề trên nhưng tại sao tôi vẫn không thể gửi được tín hiệu raw???

 Thực chất led thu của các bạn dù tốt hay kém, nó vẫn đều chứa đựng một nguồn nhiễu nhất định. Như ở ví dụ trên, tín hiệu mình thu được là 584 phần tử. Nhưng thực tế mã chính xác mà nhà sản xuất Mitsubitshi đưa ra chỉ có 580 phần tử. Việc sử dụng 1 led thu kém chất lượng sẽ khiến các bạn không nhận được tín hiệu 1 cách “trong sạch”. 

Nếu các bạn có dự án liên quan đến học tín hiệu điều khiển và gửi lại mã điều khiển. Với bất kỳ loại điều khiển hồng ngoại nào, hãy thu lại mã raw theo cách trên mình hướng dẫn và comment  lại phía dưới bài viết, mình sẽ kiểm tra và gửi lại các bạn mã raw đã được lọc nhiễu, theo đúng thông số của nhà sản xuất cho các bạn.heart

Chúc các bạn thành công!

 

lên
18 thành viên đã đánh giá bài viết này hữu ích.
Các dự án được truyền cảm hứng

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

ENERGIA -Bản sao của IDE Arduino

Các bạn đã khá quen thuộc với những bo mạch Arduino, IDE lập trình Arduino. Tuy nhiên khi nhìn thấy IDE này chắc hẳn các bạn sẽ không khỏi bất ngờ phiên bản IDE này là phiên bản nào? Câu trả lời: đó là IDE Energia, 1 thế giới khác gần như là 1 bản sao của Arduino.

Bài viết này mình xin giới thiệu tới các bạn 1 bản sao của Arduino

lên
19 thành viên đã đánh giá bài viết này hữu ích.

Điều khiển desktop (máy tính) từ xa bằng hồng ngoại - Dễ như trở bàn tay

Điều khiển máy tính bằng hồng ngoại là dự án mình đã hoàn thành cách đây 1 năm dựa trên ý tưởng "Đang nằm trên giường muốn cầm điều khiển tivi để bật, tắt, next, preview bài hát, cũng như là điều khiển chuột máy tính mà không muốn lết đến bên máy tính để làm điều tương tự bằng chuột". Nếu xét 1 cách rộng hơn, dự án này gần như là chế 1 con chuột không dây. Bài viết này mình xin giới thiệu 1 phần của dự án, phần cơ bản nhất để các bạn có thể tham khảo và phát triển thêm theo ý tưởng của mình.

lên
11 thành viên đã đánh giá bài viết này hữu ích.