Giúp người dùng:
Giao thức modbus là gì ?
Giao thức modbus là một hệ thống gồm nhiều thiết bị sử dụng cùng một giao thức để nói chuyện với nhau thông qua một cặp dây xoắn đơn. Nó hoạt động dựa trên nguyên lý master – slave (chủ – tớ). Trong đó sẽ có một thiết bị đóng vai trò là master và nhiều thiết bị slave.
Có 3 chuẩn Modbus được sử dụng phổ biến trong công nghiệp – tự động hóa là: Modbus RTU, Modbus TCP, Modbus ASCII và ở bài viết này ta sẽ sử dụng chuẩn Modbus RTU:
Hình ảnh về truyền thông Modbus RTU
Trong chuẩn Modbus RTU, các dữ liệu được mã hóa theo hệ nhị phân. Đây là chuẩn lý tưởng cho truyền thông RS232, RS485 đa điểm, tốc độ từ 1200 baud đến 115200 baud. Trong đó phổ biến nhất là 9600 baud.
Một khung truyền Modbus RTU bao gồm: 1 byte địa chỉ - 1 byte mã hàm - n byte dữ liệu - 2 byte kiểm tra lỗi khung truyền (CRC).
Byte địa chỉ (ID): Giúp master xác định được nó đang làm việc là slave nào. Các Slave sẽ được gắn địa chỉ từ 1-247 và phải đảm bảo không có Slave nào trùng địa chỉ với nhau.
Byte mã hàm (Function code): Nó sẽ chỉ định hành động Master yêu cầu Slave thực hiện như đọc / ghi một hoặc nhiều dữ liệu trong thiết bị. Dữ liệu trong các thiết bị Modbus sẽ chia thành các kiểu dữ liệu khác nhau.
Byte dữ liệu: Dữ liệu truyền đi hoặc phản hồi lại.
Byte kiểm tra lỗi khung truyền: Dùng để kiểm tra lỗi khung truyền, nó chiếm 16-bit tương ứng với 2 byte.
Các kiểu dữ liệu trong Modbus
Trong đó Coil và Discrete Input có kích thước 1 bit, giá trị của nó có thể là ON (1) hoặc OFF (0). Coil gọi là Discrete Output Coils, chứa trạng thái của các đầu ra vật lý, dữ liệu của chúng có thể đọc hoặc ghi. Discrete Input thường chứa trạng thái của một số đầu vào vật lý, dữ liệu của chúng chỉ có thể đọc.
Đối với các Register, chúng là những thanh ghi 16 bit. Giá trị của chúng nằm trong khoảng 0x0000 – 0xFFFF theo mã hexadecimal. Input Register, chỉ chứa dữ liệu đầu vào Analog tương tự, người dùng chỉ có thể đọc dữ liệu từ loại Register này. Holding Register, dữ liệu của chúng có thể được đọc hoặc ghi, có chức năng lưu trữ dữ liệu cho các thiết bị.
Ứng dụng trong thực tế
Đây là một giao thức được sử dụng rộng rãi trong nhiều lĩnh vực tự động hóa, công nghiệp.... vì những ưu điểm ổn định - đơn giản - dễ dùng.
Trong hệ thống, nhà máy có rất nhiều thiết bị cảm biến, đo lường: Cảm biến độ ẩm, cảm biến áp suất, cảm biến nhiệt độ. Chưa kể đến các thiết bị công nghiệp nặng khác. Không thể kéo nhiều dây nguồn, tín hiệu đi đường xa sẽ gây nhiễu cao, tốn kém. Sử dụng Modbus đường truyền giúp ổn định đường truyền xa, tiết kiệm chi phí, giám sát theo thời gian thực, dễ thi công và quản lý. Trên tài nguyên mạch C88 được tích hợp sẵn 1 bộ truyền thông nối tiếp UART, khi sử dụng kết hợp với module RS485 là có thể truyền thông được modbus.
/* Lưu ý cho người mới tiếp cận với lập trình: ngôn ngữ lập trình cho mạch C88 là C/C++ nên chúng ta cần lưu ý phân biệt chữ hoa chữ thường và các dấu chấm, dấu phẩy, dấu chấm phẩy... */
Để sử dụng giao thức Modbus, ta cần cài đặt thêm thư viện: “ArduinoModbus.h”.
Link cài đặt: https://4dot0.net/upload/file/MODBUS.zip
Hàm sử dụng cho Client:
a. Hàm requestFrom()
Cấu trúc: ModbusRTUClient.requestFrom(a, b, c, d);
Trong đó:
+) a là địa chỉ đích đến được chọn. Modbus hoạt động trên một bus, có nghĩa là có thể có nhiều sever ở các địa chỉ khác nhau.
+) b là dạng COILS (giá trị số), DISCRETE_INPUTS (giá trị vào số),
HOLD_REGISTERS (giá trị đầu vào hoặc ra tương tự analog) hoặc INPUT_REGISTERS (giá trị đầu vào tương tự analog).
+) c là địa chỉ bắt đầu sử dụng cho thao tác.
+) d là số thanh ghi cần đọc.
Ý nghĩa: Yêu cầu đọc giá trị:
+) Trả về 0 khi thất bại.
+) Trả về giá trị đọc được khi thành công.
Ứng dụng: Yêu cầu, truy cập đọc dữ liệu từ bên bo Server gửi về.
Ví dụ: Khai báo đọc dữ liệu từ bên Server.
b. Hàm read()
Cấu trúc: ModbusRTUClient.read();
Ý nghĩa: Sử dụng để đọc dữ liệu:
+) Trả về -1 khi thất bại.
+) Trả về giá trị khi thành công.
Ứng dụng: Đọc một giá trị nhận được sau khi gọi hàm requestFrom…
Ví dụ: Đọc dữ liệu.
c. Hàm beginTransmission()
Cấu trúc: ModbusRTUClient.beginTransmission(a, b, c, d);
Trong đó:
+) a là Địa chỉ đích đến được chọn. Modbus hoạt động trên một bus, có nghĩa là có thể có nhiều sever ở các địa chỉ khác nhau.
+) b là dạng COILS (giá trị số), HOLD_REGISTERS (giá trị tương tự analog).
+) c là địa chỉ bắt đầu sử dụng cho thao tác.
+) d là số thanh ghi cần ghi.
Ý nghĩa: Truyền dữ liệu, giá trị trả về 1 khi thành công và 0 khi thất bại.
Ứng dụng: Yêu cầu truyền dữ liệu.
Ví dụ: Khai báo truyền dữ liệu tới Server.
d. Hàm write()
Cấu trúc: ModbusRTUClient.write(x);
Trong đó: x là giá trị hoạt động truyền đi dữ liệu bắt đầu bởi beginTransmission ().
Ý nghĩa: Ghi, truyền dữ liệu. Hàm trả về giá trị là 1 khi thành công và 0 khi thất bại.
Ví dụ:
e. Hàm coilWrite()
Cấu trúc: ModbusRTUClient.coilWrite(a, b, c);
Trong đó:
+) a là địa chỉ đích đến được chọn. Modbus hoạt động trên một bus, có nghĩa là có thể có nhiều sever ở các địa chỉ khác nhau.
+) b là địa chỉ bắt đầu sử dụng cho thao tác.
+) c là giá trị truyền.
Ý nghĩa: Ghi, truyền dữ liệu đi dạng số (digital).
Ví dụ:
Sử dụng cho Server:
a. Hàm poll()
Cấu trúc: ModbusRTUServer.poll();
Ý nghĩa: Thực hiện các yêu cầu yêu được nhận.
Ứng dụng: Thực hiện quá trình nhận dữ liệu.
Ví dụ:
b. Hàm configureCoils()
Cấu trúc: ModbusRTUServer.configureCoils(x, y);
Trong đó:
+) x là địa chỉ cấu hình.
+) y là số thanh ghi dữ liệu đầu vào / ra cấu hình.
Ý nghĩa: Cấu hình giá trị số, giá trị hàm trả về 0 khi thành công và 1 khi thất bại
Ứng dụng: Định nghĩa, cấu hình cho máy chủ.
Ví dụ:
c. Hàm configureInputRegisters()
Cấu trúc: ModbusRTUServer.configureInputRegisters(x, y);
Trong đó:
+) x là địa chỉ cấu hình thanh ghi đầu vào.
+) y là số thanh ghi dữ liệu đầu vào cấu hình.
Ý nghĩa: Cấu hình truyền, nhận giá trị Analog, giá trị hàm trả về 0 khi thành công và 1 khi thất bại.
Ứng dụng: Định nghĩa, cấu hình cho máy chủ.
Ví dụ:
d. Hàm coilRead()
Cấu trúc:
ModbusRTUServer.coilRead(x, y);
ModbusRTUServer.coilRead(y);
Trong đó:
+) x là địa chỉ đích đến được chọn. Modbus hoạt động trên một bus, có nghĩa là có thể có nhiều sever ở các địa chỉ khác nhau.
+) y là địa chỉ đọc dữ liệu và tín hiệu đọc được là dạng số (Digital), được chỉ định thiết lập.
Ý nghĩa: Hàm trả về giá trị đọc được khi thành công, -1 khi thất bại.
Ứng dụng: Đọc dữ liệu từ bo Client gửi.
Ví dụ:
e. Hàm discreteInputWrite()
Cấu trúc: ModbusRTUServer.inputRegisterWrite(x, y);
Trong đó:
+) x là địa chỉ sử dụng cho giá trị truyền đi.
+) y là giá trị thanh ghi đầu vào analog truyền đi.
Ý nghĩa: Truyền dữ liệu analog đi, hàm trả về 1 khi thành công và 0 khi thất bại.
Ứng dụng: Truyền dữ liệu thanh ghi đầu vào analog đi.
Ví dụ:
Bài 1.
Thiết kế, viết chương trình bo Master truyền dữ liệu đi để điều khiển đầu ra rơ le đầu ra R.0 trên bo Slave.
a. Chuẩn bị
- 2 bo C88.
- 2 module RS485 TTL - 01.
b. Sơ đồ lắp ráp
c. Mã lệnh
Chương trình Client:
/* Modbus RTU Client*/
// Thư viện đầy đủ Modbus
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
void setup() {
Serial.begin(9600);
while (!Serial);
// Khởi động máy khách Modbus RTU client
ModbusRTUClient.begin(9600); // thiết lập tốc độ 9600
}
void loop() {
// Đối với (slave) id là 1, địa chỉ: 0x00, ghi giá trị: 0x01
ModbusRTUClient.coilWrite(1, 0x00, 0x01);
delay(1000); // tạo trễ 1 giây.
// Đối với (slave) id là 1, địa chỉ: 0x00, ghi giá trị: 0x00
ModbusRTUClient.coilWrite(1, 0x00, 0x00);
delay(1000);
}
Chương trình Server:
/* Modbus RTU Server*/
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
#define R0 4 // định nghĩa địa chỉ D4 tên R0
void setup() {
Serial.begin(9600); // khởi tạo tốc độ 9600
pinMode(R0, OUTPUT); // Cấu hình chiều ra cho R0
// Khởi động Modbus RTU server, với (slave) id là 1
ModbusRTUServer.begin(1, 9600); // Tốc độ truyền nhận 9600
// cấu hình địa chỉ 0x00
ModbusRTUServer.configureCoils(0x00, 1);
}
void loop() {
ModbusRTUServer.poll(); // bắt đầu thực hiện các yêu cầu của bo Client gửi về
int coilValue = ModbusRTUServer.coilRead(0x00); // hàm đọc dữ liệu
if (coilValue == 0x01) { // nếu nhận được giá trị 0x01
digitalWrite(R0, HIGH); // bật rơ le R.0
}
else { // ngược lại
digitalWrite(R0, LOW); // tắt rơ le R.0
}
}
d. Kết quả (Mạch hoạt động thế nào)
Link video: https://youtu.be/3VIC9H3QhtQ
Bài 2.
Thiết kế, viết chương trình bo Client yêu cầu bên bo Server phản hồi nhiệt độ về hiển thị trên LCD.
a. Chuẩn bị
- 2 bo C88.
- 2 module RS485 TTL - 01.
- Cảm biến DHT11 hoặc DHT22 <Link cài thư viện>: https://4dot0.net/upload/file/DHT.zip
b. Sơ đồ lắp ráp
Kết nối:
+) Module RS485 TTL - 01 như phần thực hiện 1.
+) LCD với mạch C88 - Client (GND với GND, VCC với +5, SDA với SDA, SCL với SCL).
+) Cảm biến DHT 22 với mạch C88 - Server (- với GND, + với +5, OUT với SDA).
Hình ảnh:
c. Mã lệnh
Chương trình Client:
/* Modbus RTU Client*/
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
float t; // sử dụng biến t
byte degree[8] = { // tạo kí tự: độ C
0B01110,
0B01010,
0B01110,
0B00000,
0B00000,
0B00000,
0B00000,
0B00000
};
void setup() {
Serial.begin(9600);
while (!Serial);
// Khởi động máy khách Modbus RTU client
ModbusRTUClient.begin(9600);
lcd.init(); // khởi động lcd
lcd.backlight(); // bật màn hình lcd
lcd.createChar(1, degree); // khởi tạo kí tự.
}
void loop() {
int x = ModbusRTUClient.coilWrite(0x01, 0x02, 0x01); // truyền dữ liệu
if (x == 0x01)
{
ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 0X01, 1);
delay(8);
t = ModbusRTUClient.read(); // đọc nhiệt độ
t = t / 10; // lấy số thực
lcd.setCursor(0, 0);
lcd.print("NHIET DO:");
lcd.print(t);
lcd.write(1);
lcd.print("C");
delay(1);
}
else
{
if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 0X01, 1)) {
lcd.setCursor(0, 0);
lcd.print("NHIET DO A0:");
lcd.setCursor(12, 0);
lcd.print("Loi!");
}
}
delay(100);
x = 0x00;
}
Chương trình Server:
/* Modbus RTU Server*/
#include "DHT.h" // thư viện DHT
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
#define DHTPIN 18 // sử dụng chân 18 - SDA
#define DHTTYPE DHT22 // DHT 22 (AM2302)
float t;
DHT dht(DHTPIN, DHTTYPE); // thiết lập DHT22
void setup() {
Serial.begin(9600);
dht.begin(); // khởi tạo chạy DHT22
// Khởi động Modbus RTU server, với (slave) id là 0x01 , tốc độ 9600
ModbusRTUServer.begin(0x01, 9600);
// cấu hình địa chỉ thanh ghi dữ liệu
ModbusRTUServer.configureInputRegisters(0X01, 1);
ModbusRTUServer.configureCoils(0x02, 1);
}
void loop() {
ModbusRTUServer.poll();
t = dht.readTemperature();
t = t*10;
int x = ModbusRTUServer.coilRead(0x02); // đọc dữ liệu
if (isnan(t)) { // nếu không đọc được
while(1);
}
if(x == 0x01)
{
// truyền giá trị analog
ModbusRTUServer.inputRegisterWrite(0X01, t); // truyền dữ liệu nhiệt độ
}
delay(200);
}
c. Kết quả (Mạch hoạt động thế nào)
Link video: https://www.youtube.com/watch?v=fG_AFv_9cgk
Hệ thống hiển thị nhiệt độ đo được từ môi trường lên App Bluetooth (Hệ điều hành android) sử dụng 2 mạch C88 truyền thông Modbus RTU.
a. Chuẩn bị
- 2 bo C88.
- 2 module RS485 TTL - 01.
- Cảm biến DHT11 hoặc DHT22.
- Module Bluetooth HC – 05.
- Link cài đặt App: https://4dot0.net/upload/file/temp_c881.apk
b. Sơ đồ lắp ráp
c. Mã lệnh
Chương trình bo trung tâm (Client)
#include <SoftwareSerial.h>
SoftwareSerial blue(10, 11); // RX, TX
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
float t;
byte degree[8] = { // tạo kí tự: độ C
0B01110,
0B01010,
0B01110,
0B00000,
0B00000,
0B00000,
0B00000,
0B00000
};
void setup() {
Serial.begin(9600);
blue.begin(9600);
blue.print("Hello, world!");
while (!Serial);
// Khởi động máy khách Modbus RTU client
ModbusRTUClient.begin(9600);
lcd.init(); // khởi động lcd
lcd.backlight(); // bật màn hình lcd
lcd.createChar(1, degree); // khởi tạo kí tự.
lcd.setCursor(0, 0);
lcd.print("NHIET DO A0:");
}
void loop() {
int x = ModbusRTUClient.coilWrite(0x01, 0x02, 0x01);
if (x == 0x01)
{
ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 0X01, 1);
delay(8);
t = ModbusRTUClient.read();
t = t / 10;
lcd.setCursor(0, 0);
lcd.print("NHIET DO A0:");
lcd.print(t);
lcd.write(1);
lcd.print("C");
delay(1);
}
else
{
if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 0X01, 1)) {
lcd.setCursor(0, 0);
lcd.print("NHIET DO A0:");
lcd.setCursor(12, 0);
lcd.print("Loi!");
}
}
delay(100);
x = 0x00;
if (isnan(t)) {
Serial.print("Loi");
}
else {
blue.print("S");
blue.print(";");
blue.print(t);
blue.print(";");
}
}
Chương trình bo tớ (Server)
/* Modbus RTU Server*/
#include "DHT.h" // thư viện DHT
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
#define DHTPIN A4 // sử dụng chân A4
#define DHTTYPE DHT22 // DHT 22 (AM2302)
float t;
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(9600);
dht.begin(); // khởi tạo chạy DHT22
ModbusRTUServer.begin(0x01, 9600);
// cấu hình địa chỉ truyền 0x00, số lượng: 1
ModbusRTUServer.configureInputRegisters(0X01, 1);
ModbusRTUServer.configureCoils(0x02, 1);
}
void loop() {
ModbusRTUServer.poll();
t = dht.readTemperature();
t = t * 10;
int x = ModbusRTUServer.coilRead(0x02);
if (isnan(t)) {
while (1);
}
if (x == 0x01)
{
ModbusRTUServer.inputRegisterWrite(0X01, t);
}
delay(200);
}
d. Kết quả (Mạch hoạt động thế nào)
Bo tớ sẽ sử dụng cảm biến để đo nhiệt độ môi trường xung quanh và đồng thời truyền qua đường Modbus về bo trung tâm dữ liệu nhiệt độ. Bo trung tâm nhận được dữ liệu sẽ hiển thị lên LCD và APP trên điện thoại
Link Video hướng dẫn và hoạt động của mạch: https://youtu.be/qfSLrGXLO0E