Serial.available vì sao lúc bằng 2 lúc bằng 1

Chuẩn giao tiếp truyền thông nối tiếp UART trên Arduino [hay còn được biết đến với tên gọi Serial] là chuẩn giao tiếp được sử dụng rất nhiều trong các ứng dụng hệ thống nhúng. Trong bài viết này, mình sẽ hướng dẫn các bạn tiếp cận và lập trình với giao tiếp UART một cách đơn giản nhất, còn về cơ bản sâu hơn thì các bạn xem tại bài viết này.

Khai báo UART: 
– Có 2 cách để khai báo sử dụng UART trên Arduino, nhưng phổ biến nhất là Serial.begin[9600], trong đó 9600 là tốc độ baud và sử dụng khung truyền mặc định 8-N-1 [8 bit dữ liệu, không sử dụng bit kiểm tra chẵn lẻ, 1 bit kết thúc]. Bạn có thể tìm hiểu kĩ hơn tại đây.  Đồng thời việc khai báo này cũng chuyển chân digital 0 và digital 1 thành chức năng truyền nhận dữ liệu: Chân digital 0 được gắn với bộ nhận dữ liệu bên trong vi điều khiển và chân digiatal 1 được nối với bộ truyền dữ liệu bên trong vi điều khiển.


Nối dây 2 thiết bị: 
– Nếu các bạn sử dụng Board Arduino để giao tiếp với máy tính [sử dụng cửa sổ Serial monitor trên ARDUINO IDE] thì không cần phải nối thêm gì cả vì trên board Arduino đã thực hiện sẵn việc đó, chỉ cần cắm cab USB vào là được. Để hiểu thêm về cấu trúc của board Arduino thì các bạn có thể xem qua bài viết này.
– Nếu các bạn sử dụng Arduino để giao tiếp với 1 thiết bị, 1 module khác thì các bạn nối chéo, TX – RX và RX – TX và đừng quên 1 điều là kiểm tra xem 2 thiết bị đã chung GND chưa, vì nếu chưa thì chúng sẽ không hiểu mức logic của nhau ==> không giao tiếp được.
– Phức tạp hơn tí nữa là nếu như 2 thiết bị khác mức logic, ví dụ như 1 thiết bị 5v giao tiếp với 1 thiết bị 3.3V thì các bạn cần thêm các mạch để chuyển đổi điện áp cho phù hợp.

Truyền dữ liệu:
– Để truyền dữ liệu thì các bạn sử dụng hàm Serial.print[x]; trong đó x là cái mà các bạn muốn in lên ở bất kì kiểu dữ liệu gì cũng được, rất tiện lợi. Nếu bạn muốn truyền thêm ký tự kết thúc câu [\r]  và ký tự xuống dòng [\n] thì sử dụng Serial.println[x] là được.

Nhận dữ liệu: 

– Nền tảng Arduino đã hỗ trợ người dùng rất nhiều khi đã xây dựng sẵn 1 bộ đệm UART [buffer] có kích cỡ 64byte[Arduino Uno]. Mỗi lần nhận 1 ký tự thì ký tự này sẽ được tự động chuyển vào bộ đệm. Người dùng muốn đọc dữ liệu chỉ cần làm việc với bộ đệm là được. Vậy làm việc với bộ đệm như thế nào? + Kiểm tra bộ đệm: Trước khi thực hiện thao tác đọc và xử lý dữ liệu của thiết bị khác gửi đến thì nên kiểm tra bộ đệm trước. Câu lệnh Serial.available[] sẽ trả về cho các bạn số kí tự [byte] hiện có trong bộ đệm. ==>Bạn sử dụng lệnh: if[Serial.available[]] {đọc và xử lý dữ liệu khi có dữ liệu} + Đọc dữ liệu: Để đọc 1byte từ bộ đệm các bạn sử dụng lệnh Serial.read[]; Tuy nhiên trong thực tế thì hầu hết chúng ta cần đọc 1 chuỗi ký tự. Các bạn có thể đọc từng ký tự, sau đó ghép chúng lại thành 1 chuỗi hoặc sử dụng kiểu dữ liệu String mà Arduino hỗ trợ [lưu ý String chữ S viết hoa]. Lệnh Serial.readString[] sẽ giúp các bạn đọc được tất cả các kí tự có trong bộ đệm.

+ Lưu ý: Bộ đệm UART sẽ mất dữ liệu ngay sau khi các bạn đọc. Vì vậy bạn nên đọc và lưu ra 1 biến và làm việc với biến đó. 

Kiểu String:
Kiểu String trong Arduino giúp cho người dùng thao tác với chuỗi đơn giản hơn với rất nhiều hàm hỗ trợ. Mình giới thiệu 1 số hàm phổ biến mà các bạn thường gặp tại đây. 

1. myString.indexOf[val]: Hàm này sẽ giúp bạn tìm được vị trí của 1 ký tự hoặc 1 chuỗi [val] trong 1 chuỗi khác [myString]. Xem chi tiết tại đây. 
2. myString.toInt[]: Từ trên màn hình Serial Monitor các bạn bấm 168 để gửi đi thì Arduino sẽ nhận được chuỗi “168”, để khôi phục lại thành số 168 thì các bạn sử dụng hàm toInt[]. Xem chi tiết hơn tại đây.
3. substring[]: Trong một chuỗi dữ liệu lớn nhận được thì sẽ có phần dữ liệu nằm trong chuỗi đó mà các bạn cần tách ra để sử dụng. Lúc này hàm substring sẽ là trợ thủ đắc lực cho bạn. Xem chi tiết hơn tại đây.
4. myString.toCharArray[]: Hàm này giúp các bạn chuyển chuỗi ở kiểu String thành 1 mảng kiểu char. Khi làm việc với một số thư viện, người phát triển đã khai báo sẵn các hàm có các tham số kiểu char *, lúc này các bạn có thể truyền vào char * hoặc char array, còn String thì không được chấp nhận, vì vậy việc chuyển đổi kiểu dữ liệu là cần thiết, xem thêm tại đây. Khi chuyển String thành mảng thì các bạn có thể sử dụng thêm hàm length[] để xác định được số lượng phần tử của mảng, xem thêm tại đây. [sau đó nhớ cộng thêm 1 vì sử dụng mảng kiểu char để lưu chuỗi thì cần có ký tự Null sau cùng].


Nếu có thắc mắc gì, các bạn cứ bình luận để thảo luận thêm cho rõ nha.  Chúc các bạn thành công!
Xem thêm:  Tổng hợp hướng dẫn Internet of Things với NodeMCU ESP8266 và ESP32

Thuong Nguyen

Serial.begin[speed]:

Tham số:

speed: tốc độ giao tiếp.

Trả về:

không.

Để bắt đầu sử dụng được chức năng serial bạn cần phải khởi động nó [nếu không khởi động thì mặc định 2 chân Rx[0] và Tx[1] đảm nhận chức năng nhận tín hiệu logic].

Đây là những tốc độ được arduino hổ trợ, bạn có thể tham khảo:

300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200…

Ngoài ra bạn có thể khởi động serial theo cách nâng cao như sau:

void setup[] { Serial.begin[9600,SERIAL_8N1]; } void loop[] { }

Trong đó SERIAL_8N1 là thiết lập nâng cao, số 8 là số lượng bit được gửi trong một phiên, ký tự tiếp theo cho biết có thực hiện kiểm tra sự đúng đắn của dữ liệu nhận được hay không [N: không kiểm tra, O: kiểm tra lẻ, E: kiểm tra chẵn] số tiếp theo cho biết số bit stop được gửi kèm.

Arduino hổ trợ các thiết lập sau:

SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1 [the default], SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2.

Serial.end[]:

Cú pháp:

Serial.end[]:

Tham số:

không.

Trả về:

không.

Trong quá trình hoạt động, nếu ta không muốn giao tiếp này hoạt động thì có thể đóng lại để tiết kiệm năng lượng [rất nhỏ, chỉ phù hợp với thiết bị di động] hoặc để sử dụng 2 chân 0 và 1 vào mục đích khác [trường hợp này rất ít khi gặp].

Lưu ý là mặc định serial không được khởi động, vậy nên bạn không cần phải đóng nó nếu chưa khởi động.

Serial.available[]:

Cú pháp:

Serial.available[].

Tham số:

không.

Trả về

số byte dữ liệu có trong bộ đệm.

Hàm này trả về số byte dữ liệu có trong bộ đệm. Nó thường được dùng để xét xem đã có dữ liệu chưa, hoặc lượng dữ liệu cần nhận đã đủ chưa.

Bộ đệm [bộ nhớ đệm] là gì?. Là một vùng nhớ được Arduino dành để lưu tạm dữ liệu nhận được. Bộ đệm được sử dụng bởi vì việc truyền dữ liệu có thể sảy ra liên tục, nhưng việc xử lý dữ liệu có thể bị ngắt quãng, lúc dữ liệu chưa kịp xử lý thì nó sẽ được lưu trong bộ đệm để khi nào CPU sẵn sàng thì cứ việc lấy ra mà xử lý, tránh được trường hợp thất thoát dữ liệu. Mình có một ví dụ để bạn có thể dễ hình dùng về bộ đệm: Giả sử cả đất nước Việt Nam không có một cách gì để trữ lúa. Như thế lượng lúa chúng ta gặt xong phải dùng hết luôn hay sao? Chính vì thế nên người ta phải xây dựng những kho chứa lúa để khi nào cần thì chỉ việc lấy ra dùng thôi. Bộ đệm trong arduino cũng có chức năng tương tự.

Serial.availableForWrite[]:

Cú pháp:
Serial.availableForWrite[].

Tham số:

không.

Kiểu trả về

số lượng byte còn lại của bộ đệm gửi.

Hàm này sẽ trả về số lượng byte còn có thể “nhét” vào bộ đệm để serial gửi đi. Nếu con số này bằng 0 thì không có nghĩa là dữ liệu của bạn sẽ không được gửi đi, arduino sẽ tự tạo thêm một bộ đệm nữa để tiếp tục quá trình gửi nếu còn bộ nhớ. Để tránh trường hợp thiếu bộ nhớ thì bạn bạn phải chờ một khoảng thời gian để serial gửi bớt dữ liệu thì mới “nhét” thêm dữ liệu vào.

Bộ đệm gửi trong Arduino uno R3 có kích thước là 64 byte.

Serial.find[]:

Cú pháp:

Serial.find[str]:

Tham số:

str: chuỗi cần tìm.

Trả về:

true nếu tìm thấy chuỗi, false khi không thấy chuỗi hoặc quá thời gian quy định.

Hàm này sẽ liên tục đọc bộ đệm cho đến khi gặp được chuỗi cần tìm hoặc quá thời gian quy định. Chính vì việc đọc liên tục như vậy nên tất cả dữ liệu trước chuỗi cần tìm sẽ mất [kể cả chuỗi cần tìm]. Ví dụ: bộ đêm đang chứa dữ liệu “abcdefghiklm”, thì sau khi chạy hàm Serial.find[“def”]: bộ đêm sẽ còn “ghiklm”.

Lưu ý là cho dù hàm này đã tìm hết dữ liệu trong bộ đệm mà chưa có chuỗi cần tìm thì nó vẫn chạy cho đến khi hết thời gian quy định chứ không dừng lại.

Serial.findUntil[]:

Cú pháp:

Serial.findUntil[str0, str1]:

Tham số:

str0: chuỗi cần tìm.

str1: chuỗi kết thúc quá trình tìm.

Trả về:

true nếu tìm thấy chuỗi, false khi không thấy chuỗi hoặc quá thời gian quy định, hoặc gặp chuỗi kết thúc quá trình tìm.

Hàm này sẽ liên tục đọc bộ đệm cho đến khi gặp được chuỗi cần tìm hoặc quá thời gian quy định, hoặc gặp chuỗi kết thúc quá trình tìm. Lưu ý là hàm này sẽ không làm mất chuỗi kết thúc quá trình tìm trong bộ đêm. Ví dụ: bộ đêm đang chứa dữ liệu “abcdefghiklm”, thì sau khi chạy hàm Serial.findUntil[“def”,”ikl”]: bộ đệm sẽ còn “ghiklm”, [kết quả tương tự như hàm Serial.find[] nhưng thời gian tìm sẽ nhanh hơn].

Serial.flush[]:

Cú pháp:

Serial.flush[]:

Tham số:

không.

Trả về:

không.

Hàm này có chức năng chờ cho đến khi dữ liệu trong bộ đệm gửi được gửi đi hết.

Serial.parseFloat[]:

Cú pháp:

Serial.parseFloat[]:

Tham số:

không.

Trả về:

float.

Hàm này có chức năng trả về số thực đầu tiên trong bộ đệm nhận mà nó thấy được, các ký tự để nhận biết số thực gồm các số , “-“, “+” và “.” [lưu ý là không có ký tự “e”, cũng như “E”].  Những dữ liệu phía trước số thực tìm được sẽ bị mất, ví dụ: nếu trong bộ đệm có “abcd123.45efghiklm” thì sau khi thực hiện Serial.parseFloat[], bộ đêm sẽ còn “efghiklm”.

Lưu ý là những ký tự  “-” và “.” phía sau số float hoàn chỉnh nhận được thì vẫn được giữ nguyên. Ví dụ: nếu trong bộ đệm có “abcd123.45.efgh” thì sau khi thực hiện Serial.parseFloat[], bộ đêm sẽ còn “.efgh”.

Serial.parseInt[]:

Cú pháp:

Serial.parseInt[]:

Tham số:

không.

Trả về:

long.

Hàm này có chức năng trả về số nguyên đầu tiên trong bộ đệm nhận mà nó thấy được, các ký tự để nhận biết số thực gồm các số và dấu “-” [lưu ý là không có ký tự “e”, cũng như “E”].  Những dữ liệu phía trước số nguyên tìm được sẽ bị mất, ví dụ: nếu trong bộ đệm có “abcd12345efghiklm” thì sau khi thực hiện Serial.parseInt[], bộ đệm sẽ còn “efghiklm”.

Lưu ý là kiểu trả về là long chứ không phải int.

Serial.peek[]:

Cú pháp:

Serial.peek[]:

Tham số:

không.

Trả về:

int.

Hàm này sẽ trả về byte đầu tiên trong bộ đệm [tương tự như hàm Serial.read[]] nhưng không tự xóa byte đó. Vì thế nên bạn không thể đọc được ký tự thứ 2 trở đi nếu cứ dùng hàm này.

Serial.write[];

Cú pháp: 1. Serial.write[val]; 2. Serial.write[str];

3. Serial.write[buf, len];

Video liên quan

Chủ Đề