Arduino – Một số dự án tham khảo

Chương này giới thiệu những ứng dụng thực tế trong cuộc sống, những ứng dụng này hầu hết sử dụng các kiến thức điện tử cơ bản và các chuẩn giao tiếp chúng ta đã học được ở các chương trước đồng thời có thêm kiến thức về các quá trình thi công, lắp mạch trong các dự án thực tế.

Những ví dụ mẫu về ứng dụng thực tế khi sử dụng Arduino:

+ Dự án điều khiển xe mô hình thông qua chuẩn giao tiếp Bluetooth.

+ Dự án bật tắt thiết bị trong nhà thông qua chuẩn giao tiếp WiFi.

Điều khiển xe tự động bằng module Bluetooth

Trong các phần trước, chúng ta đã tìm hiểu được các giao thức truyền nhận dữ liệu giữa Arduino với các thiết bị truyền nhận thông qua các chuẩn giao tiếp có dây. Ở phần này, chúng ta sẽ tìm hiểu những kiến thức cơ bản về chuẩn giao tiếp không dây Bluetooth thông qua ứng dụng điều khiển xe tự động. Đồng thời, cũng sẽ làm quen với các kiến thức cơ bản về điều khiển bằng xung PWM, điều khiển động cơ DC.

Cơ bản về ứng dụng điều khiển xe tự động

Bạn đọc chắc hẳn khá quen thuộc với các xe đồ chơi điều khiển từ xa. Phần lớn các xe đồ chơi điều khiển từ xa giá rẻ được bán trên thị trường ngày nay đều được điều khiển bằng sóng vô tuyến thông qua các module RF. Bên cạnh sóng vô tuyến, việc điều khiển từ xa nói chung còn có thể được thực hiện thông qua các chuẩn giao tiếp không dây phổ biến khác là WiFi, Bluetooth và Zigbee.

Cũng như các hệ thống điều khiển thông minh khác, các xe điều khiển từ xa có một vi điều khiển làm nhiệm vụ điều khiển chuyển động của động cơ DC, các động cơ này sẽ được nối với các bánh xe được để tạo ra chuyển động cho xe. Phần lớn các xe điều khiển ngày nay hoặc các kit phát triển đều có thêm những module điều khiển động cơ giúp cho việc cấu hình, lắp đặt sẽ đơn giản hơn cho người sử dụng. Do đó, nhiệm vụ của chúng ta là điều khiển từ xa được 4 động cơ tương ứng với 4 bánh xe.

control car
Figure 1. Hình ảnh 1 dạng xe mô hình thường được sử dụng (Nguồn Amazon.ca)

Mở đầu về điều khiển động cơ DC

Động cơ DC hoạt động dựa trên định luật Ampere về tương tác lực từ. Cấu tạo và nguyên lí hoạt động của động cơ DC có thể tham khảo ở video: DC motor, how it works

dc motor
Figure 2. Hình ảnh động cơ DC

Chiều quay của động cơ phụ thuộc vào chiều dòng điện chạy trong các khung dây dẫn. Việc đảo chiều quay của động cơ có thể thực hiện bằng cách đảo chiều dòng điện cấp vào cho động cơ. Chiều quay động cơ cũng có thể được đảo lại bằng cách thay đổi chiều của từ trường qua động cơ, tuy nhiên cách làm này không phổ biến.

Tùy từng loại động cơ DC mà người dùng lựa chọn điện áp DC cấp vào để động cơ hoạt động. Việc điều khiển động cơ khá phức tạp do việc điều khiển động cơ không phải chỉ đơn giản là cấp nguồn cho động cơ hoạt động mà phải điều khiển động cơ thông qua tín hiệu ngõ ra từ vi điều khiển. Do dòng ngõ ra của các loại vi điều khiển thường khá nhỏ để đóng/cắt các transistor trong sơ đồ động lực làm quay motor, do đó vấn đề đặt ra là phải thiết kế một mạch driver phù hợp để tín hiệu ngõ ra từ vi điều khiển có thể điều khiển động cơ.

Ngày nay, nhiều module driver cho các loại động cơ như DC, Servo, động cơ bước đã được sản xuất hàng loạt và phân phối rộng rãi trên thị trường, do đó tiết kiệm nhiều thời gian cho người dùng trong việc lựa chọn, thiết kế các mạch driver, buffer.

Nếu muốn tìm hiểu chi tiết về các mạch driver, buffer trong điều khiển động cơ có thể tham khảo các kiến thức liên quan về mạch cầu H, khuếch đại tín hiệu đầu vào với transistor, các linh kiện bán dẫn liên quan như IGBT, MOSFET,…​

Ở phạm vi nghiên cứu cơ bản, chúng ta chỉ xét đến việc điều khiển động cơ DC bằng xung PWM thông qua 2 module driver là L293D và L298N.

Điều khiển động cơ với module L293D

L293D
Figure 3. Driver Motor Shield L293D

Driver Motor Shield L293D là một module mở rộng chuyên dụng cho các ứng dụng điều khiển động cơ DC, động cơ Servo, động cơ bước. Module sử dụng IC L293D, phù hợp với việc cắm trực tiếp vào các shield Arduino. Thông tin kỹ thuật của module có thể tham khảo tại: https://iotmaker.vn/driver-motorl-shield-293d.html.

Driver Motor Shield L293D có kích thước vừa với module IoT Maker UnoX. Các tín hiệu điều khiển từ board IoT Maker UnoX có thể truyền trực tiếp đến shield L293D và có thể lấy tín hiệu ngõ ra từ shield để điều khiển động cơ.

Yêu cầu

Chương trình điều khiển động cơ DC quay thuận, quay nghịch và tắt tự động cơ theo chu kỳ.

Đấu nối

Bảng đấu nối board IoT Maker UnoX và shield L293D

Để điều khiển được động cơ với shield L293D, cần cài đặt thêm thư viện AFMotor của Adafruit. Với động cơ DC, thư viện hỗ trợ các tính năng điều khiển mặc định là: FORWARD, BACKWARD và RELEASE tương ứng với các thao tác tiến (quay thuận), lùi (quay ngược) và dừng động cơ.

#include <AFMotor.h>

AF_DCMotor motor(1, MOTOR12_64KHZ); // Khởi tạo động cơ 1 với pwm 64KHz

void setup()
{
    Serial.begin(9600);
    Serial.println("Control DC motor");

    motor.setSpeed(255);
}

void loop()
{
    Serial.println("Run forward");  // Chạy tiến
    motor.run(FORWARD);

    delay(1000);

    Serial.println("Run backward");// Chạy lùi
    motor.run(BACKWARD);

    delay(1000);

    Serial.println("Stop");       // Dừng
    motor.run(RELEASE);

    delay(1000);
}

Giải thích

Chương trình trên sẽ cho motor quay thuận 1s, sau đó quay nghịch 1s và dừng 1s. Sau đó, chương trình sẽ lặp lại liên tục.

  • Lệnh AF_DCMotor motor(1, MOTOR12_64KHZ): Khởi tạo đối tượng “motor” là ngõ ra motor 1 trên shield L293D dùng để điều khiển động cơ với tần số PWM là 64KHz.

  • Lệnh motor.setSpeed(255): Đặt tốc độ tối đa cho động cơ. Cũng giống như hàm analogWrite(<pin>, 255), khi đó chân <pin> sẽ có duty cycle là 255/255 = 100%, tức là toàn bộ xung là điện áp mức 1.

  • Chiều quay động cơ trong thư viện AFMotor tương ứng với 3 thuộc tính mặc định là FORWARD (tiến/quay thuận), BACKWARD(lùi/quay nghịch), RELEASE (dừng).

  • Hàm motor.run() sẽ có 3 đối số truyền vào là FORWARD, BACKWARDRELEASE tương ứng với việc cho motor quay thuận, quay nghịch và dừng.

Điều khiển động cơ với module L298N

L298N
Figure 4. L298N Dual H-Bridge Module

L298N Dual H-Bridge Module là một module giúp người dùng điều khiển động cơ DC dễ dàng, ngoài ra module cũng có thể điều khiển được một động cơ bước lưỡng cực. Module có cấu tạo gồm một driver L298N tích hợp 2 mạch cầu H. Thông tin chi tiết về module L298N Dual H-Bridge có thể tham khảo tại: Module cầu H L298N.

Để sử dụng module, người dùng không cần dùng thêm thư viện hỗ trợ nào do các hàm mặc định của Arduino đã đủ để điều khiển. Người dùng chỉ cần kết nối đúng các chân điều khiển và nạp chương trình thì đã có thể điều khiển được motor. Với module L298N, để motor quay thuận, ta chỉ cần ghi điện áp mức 1 vào một cực của motor và mức 0 vào cực còn lại, để đảo chiều, ta chỉ cần đảo thứ tự ghi mức điện áp.

Các chân của L298N

L298N
Figure 5. Hình ảnh các chân của L298 (nguồn robotics.org.za)
  • +12V Power và +5V Power là 2 chân cấp nguồn cho motor và nguồn từ pin sẽ được cấp vào 2 chân này.

  • Có thể cấp từ 9-12VDC cho chân +12V Power và khi cấp 9-12VDC vào chân +12V chân +5V Power sẽ là chân output ra 5VDC cấp cho Aruino.

  • GND là chân GND của nguồn cấp cho motor.

  • Nếu sử dụng nguồn 5VDC của chân +5V Power cấp cho Arduino thì GND của Arduino sẽ được nối với chân này.

  • 2 Jump A enable và B enable là 2 chân giúp OUTPUT A và OUTPUT B hoạt động, nếu bạn không muốn motor nào hoạt động thì rút jump của OUTPUT motor đó ra.

  • 4 chân Input IN1, IN2, IN3, IN4 là các chân tín hiệu nhận từ arduino để điều khiển tốc độ quay, quay thuận hoặc quay nghịch.

  • OUTPUT A nối với động cơ A, OUTPUT B nối với động cơ B, nếu bạn nối ngược chiều thì động cơ sẽ chạy ngược, thấy motor chạy ngược thì bạn chỉ cần nối lại thôi.

4 motors
Figure 6. Hình ảnh kết nối module L298N để điều khiển 4 động cơ DC (Nguồn custom-build-robots.com)

Chương trình cho động cơ DC quay thuận, quay nghịch và tắt tự động theo chu kỳ

#define IN1 8 //  Chân kết trên board Iotmaker Uno X kết nối với module L298
#define IN2 9 //  Chân kết trên board Iotmaker Uno X kết nối với module L298

#define MAX_SPEED 255
#define MIN_SPEED 0

void setup()
{
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
}

void stop()
{
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
}

// Động cơ quay thuận (đi tới)
void forward(int speed) {
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);// Cố định khoảng giá trị của spped trong khoảng từ MIN_SPEED tới MAX_SPEED
  digitalWrite(IN1, HIGH);                       // digitalWrite() thường dùng cho chân không có PWM
  analogWrite(IN2, 255 - speed);                 // Dùng cho chân có PWM, ở đây là chân 9
}

//  Động cơ quay nghịch(đi lùi). Để quay nghịch, ta chỉ cần đảo chiều dòng điện cấp vào động cơ DC
void backward(int speed)
{
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, LOW);
  analogWrite(IN2, speed);
}

void loop()
{
  forward(MAX_SPEED);
  delay(5000);
  backward(MAX_SPEED);
  delay(5000);
  stop();
  delay(5000);
}

Xe điều khiển từ xa với 4 động cơ DC

Từ kiến thức về điều khiển 1 động cơ DC cơ bản đã tìm hiểu ở các phần trên. Tiếp theo, chúng ta sẽ tìm hiểu việc vận hành đơn giản với một xe điều khiển từ xa dùng 4 động cơ DC tương ứng với 4 bánh. Một ứng dụng điều khiển xe hoàn chỉnh với các tính năng chạy tiến, lùi, rẽ trái, rẽ phải thì mỗi động cơ DC ứng với mỗi bánh phải có một ngõ ra tương ứng trên shield driver. Chúng ta sẽ dùng 2 module L298N để điều khiển cả 4 động cơ DC của xe mô hình với thư viện “AFMotor.h” như đã trình bày ở trên. Ta chỉ cần kết nối 4 ngõ ra của 2 module đến 4 động cơ DC tương ứng, chú ý thứ tự IN-OUT tương ứng để điều khiển đúng chiều quay động cơ. Một số khung xe mô hình thường được sử dụng có thể tham khảo tại: Các sản phẩm robot và drone.

Để xe tiến hoặc lùi, cách đơn giản nhất là cho 4 động cơ cùng quay một chiều thuận hoặc nghịch; để dừng, ta cho cả 4 động cơ dừng chạy.
Lưu ý: chương trình dưới đây tương ứng với cách kết nối dây trong khung xe dùng cho chương trình này. Bạn đọc tham khảo chương trình này và chỉnh sửa lại cho phù hợp với kết nối dây trong khung xe của mình.

Chương trình cho xe chạy tiến 5s, chạy lùi 5s, sau đó dừng 1s rồi tiếp tục lặp lại quá trình

// Khai báo các chân kết nối với board Iotmaker Uno X
#define IN1 7
#define IN2 6
#define IN3 5
#define IN4 4
#define IN5 11
#define IN6 10
#define IN7 9
#define IN8 8
// Khai báo tốc độ maximum và minimum để cài đặt trong chương trình
#define MAX_SPEED 255
#define MIN_SPEED 0

void setup()
{
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(IN5, OUTPUT);
  pinMode(IN6, OUTPUT);
  pinMode(IN7, OUTPUT);
}
// Các hàm dùng
void motor_stop()
{
  //  Motor 1
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  //  Motor 2
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
  //  Motor 3
  digitalWrite(IN5, LOW);
  digitalWrite(IN6, LOW);
  //  Motor 4
  digitalWrite(IN7, LOW);
  digitalWrite(IN8, LOW);
}

void motor_forward(int speed)
{
  //  Motor 1
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, HIGH);
  analogWrite(IN2, 255 - speed);
  //  Motor 2
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN4, HIGH);
  analogWrite(IN3, 255 - speed);
  //  Motor 3
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN5, HIGH);
  analogWrite(IN6, 255 - speed);
  //  Motor 4
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN8, HIGH);
  analogWrite(IN7, 255 - speed);
}

void motor_backward(int speed)
{
  //  Motor 1
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, LOW);
  analogWrite(IN2, speed);
  //  Motor 2
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN4, LOW);
  analogWrite(IN3, speed);
  //  Motor 3
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN5, LOW);
  analogWrite(IN6, speed);
  //  Motor 4
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN8, LOW);
  analogWrite(IN7, speed);
}

void loop()
{
  motor_forward(MAX_SPEED);
  delay(5000);

  motor_backward(MAX_SPEED);
  delay(5000);

  motor_stop();
  delay(1000);
}
Một vấn đề có thể gặp phải khi chạy chương trình trên là sẽ có ít nhất một bánh xe quay ngược chiều với các bánh còn lại. Để quay đồng bộ chỉ cần đổi thứ tự dây cắm ngõ vào IN tương ứng với motor đó.

Điều khiển xe từ xa bằng Bluetooth

Bluetooth

Bluetooth là một chuẩn kết nối không dây tầm ngắn sử dụng sóng ở tần số 2.4 GHz để kết nối các thiết bị như điện thoại, laptop, máy tính bảng,…​ với nhau. Khi 2 thiết bị được kết nối với nhau bằng Bluetooth, người dùng có thể chia sẻ dữ liệu của 2 thiết bị đó với nhau.

Bluetooth Low Energy (BLE)

Bluetooth Low Energy (viết tắt là BLE) là một công nghệ Bluetooth tiết kiệm năng lượng hay Bluetooth năng lượng thấp. BLE còn được gọi là “Smart Bluetooth” vì BLE có thể kết nối giao tiếp được với các smartphone được hỗ trợ với các hệ điều hành khác nhau như IOS, Android, Windows Phone và BackBerry, cũng như macOS, Linux, Windows 8 và 10. So với Bluetooth Classic thì BLE giảm đáng kể về năng lượng tiêu thụ. Tuy nhiên, BLE không thể thay thế hoàn toàn các chuẩn truyền không dây khác như WiFi, Zigbee, LoRa,…​ và cả Bluetooth Classic.

BLE hoạt động ổn định trong phạm vi 10m với dữ liệu truyền tải không lớn.

Pairing

Từ định nghĩa Bluetooth ở trên, ta thấy rằng nếu có có nhiều hơn 2 thiết bị, ví dụ như nhiều điện thoại thông minh (smartphone) và nhiều thiết bị khác cùng kết nối Bluetooth thì tất cả sẽ không nhận biết nhau được. Do đó, một việc rất quan trọng mà chúng ta cần thực hiện đầu tiên là đảm bảo cho 2 thiết bị kết nối được với nhau. Việc này gọi là Pairing (ghép đôi).

Giới thiệu các module Bluetooth

Một số module Bluetooth được sử dụng rộng rãi trong việc điều khiển thiết bị là Module Bluetooth HC-06Module Bluetooth BT16 4.2 bởi sự ổn định khi truyền nhận dữ liệu và giá thành hợp lý.

Module Bluetooth HC-06

HC 06
Figure 7. Module Bluetooth HC-06

Module Bluetooth HC-06 là một module thuộc kiểu truyền thống (Bluetooth Classic) hỗ trợ giao tiếp với các vi điều khiển ở tần số 2.4Ghz. Sau khi kết nối module đến vi điều khiển, module này sẽ tự động phát Bluetooth, các thiết bị như smartphone, laptop,…​ sẽ dò được tên module là HC-06, tuy nhiên các thiết bị không ghép đôi được với module. Chỉ có một số ứng dụng Bluetooth mới ghép đôi và truyền nhận dữ liệu được với module. Người dùng có thể tìm được nhiều ứng dụng điều khiển khác nhau trên Google Play, tài liệu này sử dụng ứng dụng: Arduino bluetooth controller trên Google Play. Ứng dụng này có hỗ trợ tính năng phím điều khiển, do đó tiện lợi cho các tính năng điều khiển xe tiến, lùi, rẽ trái, rẽ phải.

rotate
Figure 8. Hình ảnh giao diện của ứng dụng Arduino Bluetooth Controller

Module Bluetooth BT16 4.2

moduleBT16
Figure 9. Module Bluetooth BT16 4.2

Module Bluetooth BT16 4.2 là một module Bluetooth tiết kiệm năng lượng (BLE) sử dụng tần số 2.4GHz. Việc sử dụng module cũng hoàn toàn tương tự như module HC-06. Tuy nhiên, để tìm kiếm 1 ứng dụng trên điện thoại để kết nối và truyền nhận dữ liệu như với module HC-06 là khá khó khăn do cộng đồng BLE vẫn còn khá ít và chưa hỗ trợ nhiều về các ứng dụng này. Do đó, các ứng dụng điện thoại để truyền nhận dữ liệu tới module hiện nay đều sử dụng việc truyền dữ liệu thủ công – tức là người dùng phải nhập vào ký tự cần truyền và nhấn gởi để gởi đến module Bluetooth BT16.

Một ứng dụng kết nối và truyền nhận dữ liệu với module BT16 được sử dụng khá phổ biến là nRF Connect. Đây là một công cụ để phát hiện các thiết bị BLE và thực hiện kết nối tới các thiết bị đó. Sau khi kết nối thành công, người dùng có thể gửi dữ liệu đến module BLE đã kết nối.

Điều khiển xe từ xa với Bluetooth

Với module BT16 4.2

Kết nối

Table 1. Kết nối IoT Maker UnoX với module BT16 4.2
IoT Maker UnoX BT16 4.2

5V

VCC

GND

GND

3

TX

2

RX

bt16 unox
Figure 10. Hình ảnh module Bluetooth BT16 4.2 với IoT Maker UnoX

Từ chương trình xe điều khiển chạy tự động ở phần trước, ta thêm một số dòng lệnh với thư viện SoftwareSerial để điều khiển motor.

Source code

#include <SoftwareSerial.h>
// Khai báo các chân kết nối với board Iotmaker Uno X
#define IN1 7
#define IN2 6
#define IN3 5
#define IN4 4
#define IN5 11
#define IN6 10
#define IN7 9
#define IN8 8
// Khai báo tốc độ maximum và minimum để cài đặt trong chương trình
#define MAX_SPEED 255
#define MIN_SPEED 0

SoftwareSerial mySerial(2, 3); // 2 chân 2,3 tương ứng với RX,TX
int state;                     // Biến lưu dữ liệu đọc từ mySerial

void setup()
{
  Serial.begin(9600);
  mySerial.begin(9600); // Khởi tạo Serial đã kết nối với chân 2 và 3 nhằm đọc dữ liệu
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(IN5, OUTPUT);
  pinMode(IN6, OUTPUT);
  pinMode(IN7, OUTPUT);

  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  digitalWrite(12, HIGH);
  digitalWrite(13, HIGH);
}

void motor_stop()
{
  //  Motor 1
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  //  Motor 2
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
  //  Motor 3
  digitalWrite(IN5, LOW);
  digitalWrite(IN6, LOW);
  //  Motor 4
  digitalWrite(IN7, LOW);
  digitalWrite(IN8, LOW);
}

void motor_forward(int speed)
{
  //  Motor 1
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, HIGH);
  analogWrite(IN2, 255 - speed);
  //  Motor 2
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN4, HIGH);
  analogWrite(IN3, 255 - speed);
  //  Motor 3
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN5, HIGH);
  analogWrite(IN6, 255 - speed);
  //  Motor 4
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN8, HIGH);
  analogWrite(IN7, 255 - speed);
}

void motor_backward(int speed)
{
  //  Motor 1
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, LOW);
  analogWrite(IN2, speed);
  //  Motor 2
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN4, LOW);
  analogWrite(IN3, speed);
  //  Motor 3
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN5, LOW);
  analogWrite(IN6, speed);
  //  Motor 4
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN8, LOW);
  analogWrite(IN7, speed);
}

void loop()
{
  if (mySerial.available() > 0) { //  Nếu có dữ liệu nhận được từ mySerial
    state = mySerial.read();      //  Đọc dữ liệu nhận được và lưu vào biến state
    Serial.println(state);        //  In ra màn hình serial dữ liệu nhận được
  }
  if (state == '1')                 //  Nếu nhận được số 1, cho xe chạy tới
    motor_forward(MAX_SPEED);
  else if (state == 2)            //  Nếu nhận được số 2, cho xe chạy lùi
    motor_backward(MAX_SPEED);
  else if (state == 3)            //  Nếu nhận được số 3, cho xe dừng
    motor_stop();
  delay(1000);
}

Giải thích code

Chương trình trên sử dụng thư viện SoftwareSerial nhằm sử dụng chân số 2 và chân số 3 tương ứng với RX, TX để truyền, nhận dữ liệu với module Bluetooth. Với chương trình trên, ta sử dụng app nRF Connect để gởi các giá trị “1”, “2”, “3” đến module Bluetooth, module sẽ truyền giá trị nhận được cho board IoT Maker UnoX thông qua UART, từ đó board IoT Maker UnoX sẽ căn cứ vào giá trị nhận được để điều khiển các motor.

  • Dữ liệu nhận được là 1 thì 4 motor quay thuận (xe chạy thẳng).

  • Dữ liệu nhận được là 2 thì 4 motor quay nghịch (xe chạy lùi).

  • Dữ liệu nhận được là 3 thì 4 motor dừng (xe dừng).

Với module HC-06

Khác với module BLE BT16, các module Bluetooth Classic có nhiều app hỗ trợ với các tính năng nút nhấn tiện lợi. Mỗi nút nhấn tương ứng với việc gởi dữ liệu tương ứng đến module Bluetooth.

Table 2. Kết nối board IoT Maker UnoX với module HC-06
Iotmaker Uno X HC-06

5V

VCC

GND

GND

3

TX

2

RX

hc06 unox
Figure 11. Hình ảnh module Bluetooth HC-06 với IoT Maker UnoX

Source code

Cũng giống như chương trình với module BT16, chương trình điều khiển xe từ xa với module bluetooth classic HC-06 hỗ trợ thêm tính năng xoay trái, xoay phải tương ứng với các nút điều khiển trên app.

#include <SoftwareSerial.h>
// Khai báo các chân kết nối với board Iotmaker Uno X
#define IN1 7
#define IN2 6
#define IN3 5
#define IN4 4
#define IN5 11
#define IN6 10
#define IN7 9
#define IN8 8
// Khai báo tốc độ maximum và minimum để cài đặt trong chương trình
#define MAX_SPEED 255
#define MIN_SPEED 0

SoftwareSerial mySerial(2, 3); // 2 chân 2,3 tương ứng với RX,TX
char state = '0';              // Biến lưu dữ liệu đọc từ mySerial

void setup()
{
  Serial.begin(9600);
  mySerial.begin(9600); // Khởi tạo Serial đã kết nối với chân 2 và 3 nhằm đọc dữ liệu
  // Cài đặt hướng ngõ ra cho các chân điều khiển động cơ
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(IN5, OUTPUT);
  pinMode(IN6, OUTPUT);
  pinMode(IN7, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  digitalWrite(12, HIGH);
  digitalWrite(13, HIGH);
}

void motor_stop()
{
  //  Motor 1
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  //  Motor 2
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
  //  Motor 3
  digitalWrite(IN5, LOW);
  digitalWrite(IN6, LOW);
  //  Motor 4
  digitalWrite(IN7, LOW);
  digitalWrite(IN8, LOW);
}

void motor_forward(int speed)
{
  //  Motor 1
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, HIGH);
  analogWrite(IN2, 255 - speed);
  //  Motor 2
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN4, HIGH);
  analogWrite(IN3, 255 - speed);
  //  Motor 3
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN5, HIGH);
  analogWrite(IN6, 255 - speed);
  //  Motor 4
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN8, HIGH);
  analogWrite(IN7, 255 - speed);
}

void motor_backward(int speed)
{
  //  Motor 1
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, LOW);
  analogWrite(IN2, speed);
  //  Motor 2
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN4, LOW);
  analogWrite(IN3, speed);
  //  Motor 3
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN5, LOW);
  analogWrite(IN6, speed);
  //  Motor 4
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN8, LOW);
  analogWrite(IN7, speed);
}
void motor_turnRight(int speed)
{
  //  Motor 1
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  //  Motor 4
  digitalWrite(IN7, LOW);
  digitalWrite(IN8, LOW);
  //  Motor 2
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN4, HIGH);
  analogWrite(IN3, 255 - speed);
  //  Motor 3
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN5, HIGH);
  analogWrite(IN6, 255 - speed);

}

void motor_turnLeft(int speed)
{
  //  Motor 2
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
  //  Motor 3
  digitalWrite(IN5, LOW);
  digitalWrite(IN6, LOW);
  //  Motor 1
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN1, HIGH);
  analogWrite(IN2, 255 - speed);
  //  Motor 4
  speed = constrain(speed, MIN_SPEED, MAX_SPEED);
  digitalWrite(IN8, HIGH);
  analogWrite(IN7, 255 - speed);
}
void loop()
{
  state = '0';
  if (mySerial.available() > 0) { //  Nếu có dữ liệu nhận được từ mySerial
    state = mySerial.read();      //  Đọc dữ liệu nhận được và lưu vào biến state
    Serial.println(state);        //  In ra màn hình serial dữ liệu nhận được
  }
  if (state == '1') {             //  Nếu kí tự nhận được là 1, xe rẽ trái
    motor_turnLeft(MAX_SPEED);
    Serial.println("turn left");
  } else if (state == '2') {
    motor_forward(MAX_SPEED);
    Serial.println("forward");
  } else if (state == '3') {
    motor_turnLeft(MAX_SPEED);
    Serial.println("turn right");
  } else if (state == '4') {
    motor_backward(MAX_SPEED);
    Serial.println("backward");
  } else if (state == '5') {
    motor_stop();
    Serial.println("stop");
  }
}

Sau khi nạp chương trình cho Arduino, người dùng sẽ sử dụng ứng dụng Arduino bluetooth controller với các phím điều khiển tương ứng để điều khiển xe. Khi mở app kết nối được với HC-06 rồi thì bạn phải đặt giá trị gửi cho các nút điều khiển, ví dụ như nút “^” là nút xe đi thẳng mà trong code nạp cho Arduino bạn xét xe đi thẳng khi nhận được giá trị là “2” thì bạn sẽ phải đặt nút “^” trong app là “2”.

Hình minh họa

app control1
app control2

Giám sát nhiệt độ, độ ẩm và bật tắt thiết bị thông qua WiFi

Yêu cầu

  • Đọc giá trị nhiệt độ, độ ẩm thông qua Cảm biến nhiệt độ, độ ẩm DHT11.

  • Tạo một điểm truy cập WiFi (Access Point) để các thiết bị có thể kết nối.

  • Các thiết bị khi kết nối WiFi này có thể giám sát được nhiệt độ, độ ẩm đọc được từ cảm biến và điều khiển các thiết bị.

Linh kiện cần dùng

Giới thiệu về ESP8266

ESP8266 là một dạng vi điều khiển tích hợp WiFi 2.4GHz (WiFi SoC) được phát triển bởi Expressif. Với giá thành rất rẻ nhưng có khả năng hoạt động như một Modem WiFi:

  • Có thể quét (scan) và kết nối đến một mạng WiFi bất kỳ (Chế độ WiFi Station) để thực hiện các tác vụ như lưu trữ, truy cập dữ liệu từ server.

  • Tạo điểm truy cập WiFi (Chế độ WiFi Access Point) cho phép các thiết bị khác kết nối, giao tiếp và điều khiển.

  • Là một server để xử lý dữ liệu từ các thiết bị sử dụng Internet khác.

Hiện nay, trên thị trường có rất nhiều phiên bản của ESP8266 nhưng trong ví dụ này, chúng ta chỉ cần sử dụng module ESP-01 với những ưu điểm như: thiết kế cực kì nhỏ gọn, dễ dàng kết nối và điều khiển, giá thành rẻ…​

ESP-01
Figure 12. Hình ảnh pinout của module ESP01 (Nguồn www.acrobotic.com)

Để board IoT Maker UnoX giao tiếp với ESP-01 ta sử dụng tập lệnh AT.

Trang web học ESP8266 với Arduino phiên bản tiếng Việt: https://arduino.esp8266.vn, trang web học ESP8266 với Arduino phiên bản tiếng Anh: http://arduino-esp8266.readthedocs.io/en/latest/.

Cảm biến DHT11

DHT11 là một cảm biến có khả năng đo nhiệt độ và độ ẩm không khí với độ chính xác vừa phải, giá cả phải chăng. Có thể lấy dữ liệu đo được của cảm biến bằng giao thức 1-Wire.

dht11
Figure 13. Hình ảnh cảm biến DHT11

Thư viện hỗ trợ lấy dữ liệu của DHT11. Dựa theo chuẩn truyền nhận 1-Wire và sự phổ biến của dòng sensor DHTXX (DHT11, DHT22,…​), có rất nhiều thư viện được xây dựng lên để việc lập trình với DHT11 trở nên dễ dàng hơn. Trong bài này chúng ta sẽ cài đặt và sử dụng thư viện DHT sensor library của Adafruit.

dht lib
Figure 14. Hình ảnh tìm kiếm và cài đặt thư viện DHT11

Ngoài ra, để sử dụng được thư viện DHT sensor library chúng ta cần thêm thư viện Adafruit Unified Sensor Driver. Sau khi tải thư viện về, bạn mở cửa sổ Arduino, chọn Sketch → Import Library…​ → Add Library…​. Sau đó chọn file .zip mà bạn vừa tải về để có thể sử dụng thư viện.

Phân tích

  • Chúng ta sẽ kết nối ESP-01 với Board Arduino bằng giao tiếp UART trên chân 10 và 11 ( Sử dụng thư viện SoftwareSerial.h).

  • ESP-01 hoạt động ở chế độ Access Point sẽ tạo một điểm phát sóng WiFi để các thiết bị khác truy cập vào.

  • Thiết bị khi kết nối WiFi của ESP-01 có thể điều khiển và giám sát thiết bị thông qua trình duyệt WEB (Google Chrome, IE, Cốc Cốc, EAGLE) với đường dẫn (link) và các chức năng liệt kê như bên dưới:

    • IPAdress: Sẽ hiển thị nhiệt độ, độ ẩm.

    • IPAdress/LED=ON: Sẽ bật LED trên board.

    • IPAddress/LED=OFF: Sẽ tắt LED trên board.

Với IPAddress là địa chỉ IP của ESP-01. Thông số này sẽ hiển thị trên SerialMonitor (Thông thường là 192.168.4.1).

Sơ đồ kết nối

Table 3. Bảng đấu nối của IoT Maker UnoX và ESP-01
IoT Maker UnoX ESP-01

3V3

8 (Vcc)

GND

1 (GND)

3

4 (RXD)

2

5 (TXD)

3V3

6 (CH_PD)

Table 4. Bảng đấu nối của IoT Maker UnoX và DHT11
IoT Maker UnoX DHT11

5V

Vcc

GND

GND

9

Data

dht11 esp01 unox
Figure 15. Hình ảnh kết nối cảm biến DHT11 và ESP-01 với board IoT Maker UnoX

Source code

#include <SoftwareSerial.h>
#include <DHT.h>

#define DEBUG false
const int DHTPIN = 9;         //  Chân đọc dữ liệu từ DHT
const int DHTTYPE = DHT11;    //  Chọn kiểu cảm biên (DHT11 hoặc DHT22))
DHT dht(DHTPIN, DHTTYPE);

SoftwareSerial Serial1(2, 3); //  10-RX, 11-TX

//  Đoạn Code HTML sẽ hiển thị lên trình duyệt.
String html =
  "<html>\
  <head>\
<title>ESP8266 Webserver</title>\
  </head>\
  <body>\
  <h1>ARDUINO STATER</h1>\
  <a>LED  </a>\
  <a href=\"/LED=ON\">ON </a>\
  <a href=\"/LED=OFF\">OFF </a>\
  </body>\
</html>";

void setup()
{
  Serial.begin(9600);     //  Khởi động Serial Monitor
  Serial1.begin(115200);  //  Khởi động UART kết nối ESP-01
  pinMode(13, OUTPUT);    //  Khai báo LED 13
  digitalWrite(13, LOW);  //  Tắt LED 13

  sendData("AT+RST\r\n", 2000, DEBUG);          //  Reset module
  sendData("AT+CWMODE=2\r\n", 1000, DEBUG);     //  Khai báo ESP-01 là Access Point
  sendData("AT+CWSAP=\"CONTROL\",\"12345678\",5,3\r\n", 5000, DEBUG); //  Tạo điểm phát Wifi với ID và Pass.
  sendData("AT+CIFSR\r\n", 1000, true);         //  Hiện IP của ESP-01
  sendData("AT+CIPMUX=1\r\n", 1000, DEBUG);     //  Configure for multiple connections
  sendData("AT+CIPSERVER=1,80\r\n", 1000, DEBUG); //  Turn on server on port 80
}

void loop()
{
  String IncomingString = "";
  boolean StringReady = false;
  while (Serial1.available()) {
    IncomingString = Serial1.readString();
    StringReady = true;
    if (StringReady) {
      Serial.println("Received String: " + IncomingString);
      if (IncomingString.indexOf("LED=ON") != -1) {
        digitalWrite(13, HIGH);
      }

      if (IncomingString.indexOf("LED=OFF") != -1) {
        digitalWrite(13, LOW);
      }
      else {
        String webpage = "<h1>Hello World</h1><a href=\"\">link text</a>";
        espsend(html);
        float temp = dht.readTemperature(); //  Lấy giá trị nhiệt độ từ cảm biến
        float humi = dht.readHumidity();    //  Lấy giá trị độ ẩm từ cảm biến
        if (isnan(temp) || isnan(humi)) {   //  Kiểm tra dữ liệu đọc được có phải là số không
          String c = "sensor is not conneted"; //  Nếu không phải là số thì báo không kết nối
          espsend(c);
        }

        else {  // Nếu là số thì hiển thị nhiệt độ và độ ẩm
          String add1 = "<h4>Temperature=</h4>";
          add1 +=  String(temp);
          add1 += "&#x2103";  // Mã HEX của độ C
          add1 += "<h4>Humidityh=</h4>";
          add1 +=  String(humi);
          add1 += "%";
          espsend(add1);
        }
      }

      String closeCommand = "AT+CIPCLOSE=0\r\n";  //  Lệnh đóng kết nối với thiết bị sau khi gửi song dữ liệu
      sendData(closeCommand, 3000, DEBUG);
    }
  }
}

// Hàm gửi dữ liệu cho thiết bị truy cập
void espsend(String d)
{
  String cipSend = " AT+CIPSEND=0,";
  cipSend += d.length();
  cipSend += "\r\n";
  sendData(cipSend, 1000, DEBUG);
  sendData(d, 1000, DEBUG);
}

// Hàm gửi lệnh và nhận dữ liệu phản hồi từ ESP
String sendData(String command, const int timeout, boolean debug)
{
  String response = "";
  Serial1.print(command);
  long int time = millis();
  while ( (time + timeout) > millis()) {
    while (Serial1.available()) {
      char c = Serial1.read(); // Đọc kí tự tiếp theo.
      response += c;
    }
  }

  if (debug) { // Nếu chọn chế độ DEBUG thì sẽ hiện lên Serial Monitor
    Serial.print("Response =" + response);
  }
  return response;
}

Nội dung của biến html là 1 đoạn code html được chuyển qua kiểu string. Chúng ta có thể tìm hiểu về HTML tại: w3schools.com.

Kết nối board với máy tính bằng cổng COM, nạp chương trình. Màn hình SerialMonitor sẽ hiển thị IPAddress của bạn như sau:

SerialMonitor

Chúng ta sẽ sử dụng IPAddress này truy cập server để đọc nhiệt độ hoặc điểu khiển thiết bị. Ví dụ điều khiển bật/tắt LED như hình dưới.

result

Tổng kết

Sau khi hoàn thành dự án này, chúng ta đã hiểu được tổng quan được cách kết nối, giao tiếp Arduino với ESP8266 đồng thời hiểu được cơ bản một Web Server hoạt động như thế nào. Đây sẽ là nền tảng để chúng ta phát triển những dự án IoT sau này.

Leave a Comment