1. Giới thiệu về ngoại vi của KIT ESP32
Người thực hiện: Nguyễn Như Hải Long
2. 1.1 Các chân GPIO của ESP32:
ESP32 có 30 chân cho phép
người dùng có thể sử dụng.
- Trong đó có 4 chân cho việc
cấp nguồn nuôi cho KIT.
- Riêng 4 chân GPIO
36,39,34,35 là các chân chỉ hỗ
trợ đầu vào.
- Chân EN được sử dụng để
reset lại KIT (ESP32 sẽ reset khi
chân EN được nối xuống GND).
- Các chân còn lại đều hỗ trợ
cả đầu vào và đầu ra.
3. 1.2 Lập trình GPIO cho ESP32:
Để sử dụng ESP32 điều khiển 1 con
led qua chân GPIO.
- Kết nối ESP32 với led như hình bên.
Trong trường hợp này, khi chân D13
(Digital13/GPIO13) xuất mức logic
LOW(0) led sẽ tắt, còn khi chân D13
xuất mức logic HIGHT(1) thì led sẽ
sáng.
4. 1.2 Lập trình GPIO cho ESP32:
Về lập trình:
- Với ESP-IDF framework ta sử dụng các hàm:
- Hàm gpio_pad_select_gpio() được
sử dụng để chọn 1 chân GPIO và
cấu hình nó để sử dụng.
- Hàm gpio_set_direction() được sử
dụng để cấu hình 1 chế độ đầu vào
hoặc đầu ra.
- Hàm gpio_set_level() được sử dụng
để ghi 1 giá trị ra chân GPIO.
Lưu ý: trong ví dụ có sử dụng hàm vTaskDelay() là
1 hàm của FreeRTOS sử dụng để tạm dùng thực thi
một hàm trong 1 khoảng thời gian nhất định (ms).
5. 1.2 Lập trình GPIO cho ESP32:
Về lập trình:
- Với Micropython ta sử dụng các hàm:
- Ta sử dụng thư viện machine.
- Hàm Pin() được sử dụng để định
cấu hình chân GPIO. Nó cho phép
chỉ định chức năng của GPIO (vào
hoặc ra) và trạng thái ban đầu của
GPIO (HIGHT or LOW).
Lưu ý: trong ví dụ có sử dụng hàm
time.sleep() là 1 hàm được sử dụng để tạm
dùng thực thi một hàm trong 1 khoảng thời
gian nhất định (s).
6. 1.2 Lập trình GPIO cho ESP32:
Trong ví dụ tiếp theo, sử dụng ESP32 để
đọc trạng thái nút nhấn hay trạng thái đầu
vào của GPIO.
- Kết nối ESP32 với 2 nút nhấn như hình
bên ở chân GPIO32 và GPIO33 (có thể tùy
chỉnh).
- Trong trường hợp này, khi nút nhấn k
được nhấn mức logic của chân GPIO sẽ là
HIGHT(1)và ngược lại khi nhấn nút nhấn,
trạng thái của chân GPIO đọc được sẽ là
LOW(0).
7. 1.2 Lập trình GPIO cho ESP32:
Về lập trình:
- Với ESP-IDF framework ta sử dụng các hàm:
- Hàm gpio_pad_select_gpio() được
sử dụng để chọn 1 chân GPIO và
cấu hình nó để sử dụng.
- Hàm gpio_set_direction() được sử
dụng để cấu hình 1 chế độ đầu vào
hoặc đầu ra.
- Hàm gpio_set_pull_mode() được sử
dụng để đặt chế độ điện trở pull-up
hoặc pull-down cho chân GPIO.
- Hàm gpio_get_level() được sử dụng
để đọc trạng thái chân GPIO.
8. 1.2 Lập trình GPIO cho ESP32:
Về lập trình:
- Với Micropython ta sử dụng các hàm:
- Ta sử dụng thư viện machine.
- Hàm Pin() được sử dụng để định
cấu hình chân GPIO. Nó cho phép
chỉ định chức năng của GPIO (vào
hoặc ra) và trạng thái ban đầu của
GPIO (HIGHT or LOW). Đặc biệt
trong ví dụ này hàm Pin() có đối
số thứ 3 là trạng thái điện trở pull-
up hoặc pull-down của GPIO.
9. 1.3 Giao tiếp I2C với ESP32
I2C protocol là 1 chuẩn giao thức truyền
thông đồng bộ 2 dây (two wire), đơn giản,
linh hoạt, tiết kiệm năng lượng.
- Sử dụng 2 đường truyền dữ liệu là SCL và
SDA tương ứng trên ESP32 là 2 chân GPIO22
và GPIO21.
- Hỗ trợ kết nối nhiều thiết bị cùng trên 1
mạch I2C và phân biệt với nhau bằng 1 địa chỉ
7bit.
- I2C hỗ trợ nhiều tốc độ khác nhau. Trong
khi truyền Master điều khiển dây SCL, Slave
điều khiển dây SDA.
Lưu ý: 2 chân SCL và SDA cần được pull-up.
10. 1.3 Giao tiếp I2C với ESP32
Về lập trình IDF:
- Thiết lập các tham số cấu hình cho
driver I2C bằng hàm i2c_param_config().
- Cài đặt driver I2C bằng hàm
i2c_driver_install().
11. 1.3 Giao tiếp I2C với ESP32
Khi cài đặt thành công driver I2C, sử dụng các API để giao tiếp với thiết bị I2C Salve:
- i2c_cmd_link_create() / i2c_cmd_link_delete() dùng để tạo/xóa đối tượng I2C command link.
- i2c_master_start() / i2c_master_stop() được dùng gửi tín hiệu bắt đầu / kết thúc truyền lệnh.
- i2c_master_write_byte() / i2c_master_read_byte() dùng ghi / đọc 1 byte qua I2C.
Ví dụ hàm đọc dữ liệu từ 1 thanh ghi với DS3231
12. 1.3 Giao tiếp I2C với ESP32
Về lập trình Micropython:
Trước tiên cần cài đặt thư viện
BME280 từ github.
- Thiết lập giao tiếp I2C bằng hàm
I2C() với các đối số truyền vào là
các chân SDA, SCL và tốc độ.
- Sử dụng phương thức
bme.read_compensated_data() để
lấy dữ liệu từ cảm biến.
13. 1.3 Giao tiếp I2C với ESP32
Tiếp theo, sử dụng ESP32 giao tiếp cùng
BME280 sensor thông quan I2C protocol.
- Kết nối chân: BME280 SCL -> GPIO22
BME280 SDA ->
GPIO21
- Chú ý địa chỉ của BME280 0x76
14. 1.3 Giao tiếp I2C với ESP32
Tiếp theo, sử dụng ESP32 giao tiếp cùng
module RTC DS3231 thông qua I2C protocol.
- Kết nối chân: RTC SCL -> GPIO22
RTC SDA ->
GPIO21
- Chú ý địa chỉ của RTC 0x68
15. 1.3 Giao tiếp I2C với ESP32
Tiếp theo, sử dụng ESP32 giao tiếp cùng
BME280 sensor thông quan I2C protocol.
- Kết nối chân: BME280 SCL -> GPIO22
BME280 SDA ->
GPIO21
- Chú ý địa chỉ của BME280 0x76
16. 1.3 Giao tiếp I2C với ESP32
Code ví dụ bằng Arduino: Giao tiếp I2C giữa ESP32 và BME280.
BME280 là cảm biến đo nhiệt độ, độ ẩm, áp suất không khí của Bosch. Cảm biến hỗ trợ cả 2
giao thức I2C và SPI. (Module hình bên dưới chỉ hỗ trợ I2C với 4 chân: VIN, GND, SCL, SDA)
17. 1.3 Giao tiếp I2C với ESP32
Kết nối ESP32 với BME280: BME280 SCL -> GPIO22 (chân clock để đồng bộ tín hiệu)
BME280 SDA -> GPIO21 (chân truyền data)
2 chân còn lại là chân 3V3-VIN (nguồn) và
GND-GND(đất)
18. 1.3 Giao tiếp I2C với ESP32
// Khai báo các thư viện cần cho dự án //
#include <Wire.h> // Thư viện Wire cho giao
tiếp I2C
#include <Adafruit_Sensor.h> // |2 thư viện này cho cảm biến
#include <Adafruit_BME280.h> // | BME280
// Định nghĩa áp suất ở mực nước biển theo đơn vị hPa là 1013.25 //
#define SEALEVELPRESSURE_HPA (1013.25)
// Khai báo đối tượng đại diện cho cảm biến BME280 là “bme” //
Adafruit_BME280 bme;
// Khai báo khoảng thời gian delay giữa các lần đo //
unsigned long delayTime;
19. 1.3 Giao tiếp I2C với ESP32
void setup() {
// Cài đặt baudrate cho Serial để in thông tin ra màn hình là 9600 //
Serial.begin(9600);
// In ra màn hình serial monitor dòng chữ “BME280 test” //
Serial.println(F("BME280 test"));
// Kiểm tra trạng thái khởi tạo bme bằng biến bool status //
bool status;
status = bme.begin(0x76); // địa chỉ I2C của bme là 0x76
// Nếu trạng thái khởi là false in ra cảnh báo //
if (status == false) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
}
// Nếu không (trạng thái khởi tạo thành công thì đặt delayTime = 1000 (1s) //
else {
Serial.println("-- Default Test --");
delayTime = 1000;
}
20. 1.3 Giao tiếp I2C với ESP32
// Định nghĩa hàm printValue() để in ra màn hình Serial Monitor các thông số
void printValue() {
// In ra màn hình nhiệt độ
Serial.print("Temperature = ");
Serial.print(bme.readTemperature());
Serial.println(" *C");
// In ra màn hình áp suất không khí
Serial.print("Pressure = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");
// In ra màn hình độ ẩm
Serial.print("Humidity = ");
Serial.print(bme.readHumidity());
Serial.println(" %");
}
21. 1.3 Giao tiếp I2C với ESP32
// Trong vòng lặp dùng hàm printValue() để in giá trị thông số
// Giữa các lần lặp delay 1 khoảng thời gian 1000ms (delayTime = 1000)
void loop() {
printValues();
delay(delayTime);
}
Kết quả in ra màn hình =======>
22. 1.3 Giao tiếp I2C với ESP32
Bài tập ứng dụng: Lập trình cho ESP32:
- Giao tiếp với BME280 sensor và module DS3231 qua giao tiếp I2C.
- In ra màn hình monitor các giá trị nhiệt độ, độ ẩm, áp suất theo thời gian.
- Báo hiệu nhiệt độ, độ ẩm vượt ngưỡng qua nhấp nháy 2 led.
23. 1.4 Giao tiếp SPI với ESP32
Giao tiếp SPI là một giao thức
truyền thông đồng bộ giữa các thiết bị
điện tử, được sử dụng để trao đổi dữ
liệu giữa các thiết bị ví dụ như vi điều
khiển với cảm biến. Đặc biệt có thể
ghép nối nhiều thiết bị.
Giao tiếp SPI được ứng dụng rộng
rãi trong các ứng dụng IoT, chẳng hạn
như trong cảm biến, màn hình, bộ
điều khiển động cơ… với độ tin cậy
cao và tốc độ truyền nhanh chóng.
24. 1.4 Giao tiếp SPI với ESP32
Giao tiếp SPI hoạt động trên các chân kết nối:
● MOSI (Master Out Slave In): tín hiệu truyền
từ Master tới Slave.
● MISO (Master In Slave Out): tín hiệu truyền
từ Slave tới Master.
● SCLK (Serial Clock): Tín hiệu xung đồng hồ
để đồng bộ hóa giữa các thiết bị.
● SS (Slave Select): Tín hiệu dùng để chọn
slave cần giao tiếp.
Trên board ESP32 có sẵn 2 cổng SPI là HSPI và
VSPI với các chân được đánh dấu trên hình bên.
25. 1.4 Giao tiếp SPI với ESP32
Tiếp theo, sử dụng ESP32 giao tiếp cùng
thẻ SD card để lưu trữ dữ liệu thông qua
giao tiếp SPI.
SD MOSI -> ESP32 MOSI (GPIO 23).
SD MISO -> ESP32 MISO (GPIO 19).
SD CLK -> ESP32 SCLK (GPIO 18).
SD SS -> ESP32 SS (GPIO 5).
Lưu ý: module khuyến khích chỉ sử dụng thẻ
sd dung lượng <2GB và trước khi sử dụng
cần format về định dạng Fat32.
26. 1.4 Giao tiếp SPI với ESP32
Code ví dụ lập trình ESP32 và SD Card
Trước khi lập trình, cần cài đặt lại
cho thẻ SD
Cài SD vào máy tính cá nhân và đi
đến muc This PC, click chuột phải
chọn Format.
Sau đó chọn FAT32 và nhấn Start
27. 1.4 Giao tiếp SPI với ESP32
Code ví dụ lập trình ESP32 và SD Card
Khai báo thư viện
sử dụng
#include "FS.h"
#include "SD.h"
#include "SPI.h"
// định nghĩa các chân sử dụng của ESP32
#define SCK 18
#define MISO 19
#define MOSI 23
#define CS 5
28. 1.4 Giao tiếp SPI với ESP32
Code ví dụ lập
trình ESP32 và
SD Card ( tiếp)
void writeFile(fs::FS &fs, const char * path, const char * message)
{
Serial.printf("Writing file: %sn", path); // lệnh in ra màn hình
File file = fs.open(path, FILE_WRITE); //mở file ở chế độ ghi
if(!file) // kiểm tra mở file thành công hay không
{
Serial.println("Failed to open file for writing");
return;
}
if(file.print(message) //ghi nội dung vao file
{
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
29. 1.4 Giao tiếp SPI với ESP32
Code ví dụ lập
trình ESP32 và
SD Card
void setup(){
Serial.begin(115200);
spi.begin(SCK, MISO, MOSI, CS);
if (!SD.begin(CS,spi,80000000)) {
Serial.println("Card Mount Failed");
return;
}
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
writeFile(SD, "/hello.txt", "Hello ");
}
void loop{}
30. 1.4 Giao tiếp SPI với ESP32
Hướng dẫn lập trình ESP32 với SD Card https://randomnerdtutorials.com/esp32-microsd-
card-arduino/#listDir
32. 1.5 Giao tiếp UART với ESP32
Truyền UART là truyền các bản tin theo chuỗi bit mà không có sự đồng bộ hóa về thời gian
(chia sẻ đồng hồ). Hai bên hiểu khi nào bắt đầu bản tin và kết thúc bản tin nhờ bit start và
bit stop. Trạng thái idle (mức cao) là trạng thái trên đường truyền không có tin gì
33. 1.5 Giao tiếp UART với ESP32
Kit ESP32 DOIT DEV KIT V1 hỗ trợ 2 cổng UART là UART0 và UART2. UART1 ít được sử dụng
hơn.
34. 1.5 Giao tiếp UART với ESP32
VD: Dùng UART để giao tiếp giữa 2 ESP32
Sơ đồ nối chân như sau:
35. 1.5 Giao tiếp UART với ESP32
#include <HardwareSerial.h>
// Định nghĩa các thông số UART
#define BAUDRATE 115200
#define RX_PIN 16
#define TX_PIN 17
void setup() {
// Start the serial communication: Baudrate, loại UART 8N1
// chân TX 17, chân RX 16
Serial2.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN);
}
void loop() {
// Gửi data qua Serial2 (TX)
char dataToSend = ‘A’;
Serial2.write(dataToSend);
delay(1000); // Cứ 1 giây gửi 1 lần
}
36. 1.5 Giao tiếp UART với ESP32
#include <HardwareSerial.h>
// Định nghĩa các thông số UART
#define BAUDRATE 115200
#define RX_PIN 16
#define TX_PIN 17
void setup() {
// Start the serial communication: Baudrate, loại UART 8N1
// chân TX 17, chân TX 16
Serial2.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN);
}
void loop() {
// Đọc data từ Serial2 (RX) buffer
while (Serial2.available()) {
char data = Serial2.read();
}
}
Editor's Notes
#4:Trong ví dụ trên, giá trị đầu vào cho hàm vTaskDelay() là 1000 / portTICKPERIODMS. Trong đó, portTICKPERIODMS là một hằng số được định nghĩa trong FreeRTOS và có giá trị tương ứng với thời gian tính bằng millisecond của một tick. Ví dụ, nếu portTICKPERIODMS là 10, thì một tick tương ứng với 10ms.
Vì thời gian tính bằng tick là cố định và không phụ thuộc vào tốc độ xử lý của hệ thống, việc sử dụng vTaskDelay() thay vì các hàm tạm dừng khác như delay() sẽ giúp đảm bảo tính ổn định và chính xác trong việc đồng bộ hóa các task trong hệ thống FreeRTOS.
#9:SDA (Serial Data Line) và SCL (Serial Clock Line).
#24:Cách hoạt động của giao tiếp SPI
Giao tiếp SPI hoạt động dựa trên một số tín hiệu chính:
MOSI (Master Out Slave In): Tín hiệu truyền dữ liệu từ master tới slave.
MISO (Master In Slave Out): Tín hiệu truyền dữ liệu từ slave tới master.
SCLK (Serial Clock): Tín hiệu đồng hồ chính, được sinh ra bởi master và được sử dụng để đồng bộ hóa truyền tải dữ liệu giữa các thiết bị.
SS (Slave Select): Tín hiệu được sử dụng để chọn thiết bị slave cần giao tiếp.
Khi master muốn giao tiếp với một thiết bị slave, nó sẽ đưa tín hiệu SS xuống mức thấp để chọn thiết bị đó. Sau đó, master sẽ sinh ra tín hiệu SCLK để đồng bộ hóa các truyền tải dữ liệu. Dữ liệu được truyền tải qua tín hiệu MOSI và MISO. Khi truyền tải xong, master sẽ đưa tín hiệu SS lên mức cao để kết thúc giao tiếp với thiết bị slave đó.