Giao tiếp ESP8266 và Arduino Uno/Mega

Chắc nhiều bạn cũng biết nhược điểm của ESP8266 là khá ít chân, do đó để mở rộng chân ra kết nối nhiều cảm biến hơn thì chúng ta cần phải kết nối thêm với chip ADC hoặc là giao tiếp qua Master-Slave của các chuẩn UART, SPI, I2C. Hôm nay mình sẽ hướng dẫn cho các bạn cách để gửi dữ liệu từ Arduino Uno/Mega sang ESP8266 thông qua những chuẩn giao tiếp kể trên.

UART

Có 2 cách để giao tiếp UART giữa ESP8266/NodeMCU: 1 là giao tiếp bằng cách code trên cả Uno/Mega và ESP8266/NodeMCU, 2 là chỉ code trên Uno/Mega, trên ESP8266/NodeMCU flash thẳng firmware AT Command, mình sẽ nói tới cách thứ 1, cách thứ 2 các bạn có thể tham khảo thêm ở bài viết https://hocarm.org/energia-bai-9-giao-tiep-esp8266-tivac/ với cách làm tương tự

Sơ đồ kết nối

Chương trình

Code NodeMCU

Gửi data '123' qua Uno/Mega, ở đây ta cần có thư viện SoftwareSerial và gán nó với 2 chân RX và TX. Sau đó cấu hình với tốc độ baud là 9600.

#include<SoftwareSerial.h> //Included SoftwareSerial Library
//Started SoftwareSerial at RX and TX pin of ESP8266/NodeMCU
SoftwareSerial s(3, 1);

void setup() {
  //Serial S Begin at 9600 Baud
  s.begin(9600);
}

void loop() {
  //Write '123' to Serial
  s.write(123);
  delay(1000);
}

Code Arduino Uno/Mega

Chương trình sau nhận thông tin, đầu tiên là tạo ra biến để nhận data, sau đó là setup baud 9600 để nhận data này.

int data; //Initialized variable to store recieved data

void setup() {
  //Serial Begin at 9600 Baud
  Serial3.begin(9600);
}

void loop() {
  data = Serial.read(); //Read the serial data and store it
  delay(1000);
}

Kết quả

Quan sát trên serial port sẽ thấy data '123' được gửi qua Uno/Mega

I2C

Khái niệm

I2C (Inter-Integrated Circuit) là giao thức kết nối giao diện bus nối tiếp. Nó cũng được gọi là TWI vì nó chỉ sử dụng hai dây để giao tiếp. Hai chân này là SDA (đường data) và SCL (đường clock).

I2C là giao thức truyền thông dựa trên sự thừa nhận, tức là phát tín hiệu kiểm tra xác nhận từ bên nhận sau khi truyền dữ liệu để biết liệu dữ liệu có được nhận thành công hay không.

Hai chế độ làm việc của I2C:

  • Master mode
  • Slave mode

Dây SDA (đường dữ liệu) được sử dụng để trao đổi dữ liệu giữa thiết bị master và thiết bị slave. SCL (đường xung nhịp đồng hồ) được sử dụng cho đồng hồ đồng bộ ở giữa thiết bị master và slave.

Thiết bị master bắt đầu giao tiếp với một thiết bị slave. Thiết bị master yêu cầu địa chỉ thiết slave để bắt đầu giao tiếp với thiết bị slave. Thiết bị Slave phản hồi cho thiết bị master khi thiết bị master được giải quyết.

Sơ sơ lý thuyết thế thôi, đi cụ thể vào thực hành ta sẽ viết chương trình Arduino cho NodeMCU là I2C master và viết chương trình Arduino cho Arduino Uno/Mega  là I2C Slave. Master sẽ gửi chuỗi hello slave đến slave và slave sẽ gửi chuổi hello master phản hồi lại.

  • Master Device: NodeMCU
  • Slave Device: Arduino Uno
  • Slave Device Address: 8

Sơ đồ kết nối

Chương trình

Code NodeMCU

#include <Wire.h>
 
void setup() {
 Serial.begin(9600); /* begin serial for debug */
 Wire.begin(D1, D2); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
}
 
void loop() {
 Wire.beginTransmission(8); /* begin with device address 8 */
 Wire.write("Hello Slave");  /* sends hello slave string */
 Wire.endTransmission();    /* stop transmitting */
 
 Wire.requestFrom(8, 13); /* request & read data of size 13 from slave */
 while(Wire.available()){
    char c = Wire.read();
  Serial.print(c);
 }
 Serial.println();
 delay(1000);
}

Code Uno/Mega

#include <Wire.h>

void setup() {
  Wire.begin(8);                /* join i2c bus with address 8 */
  Wire.onReceive(receiveEvent); /* register receive event */
  Wire.onRequest(requestEvent); /* register request event */
  Serial.begin(9600);           /* start serial for debug */
}

void loop() {
  delay(100);
}

// function that executes whenever data is received from master
void receiveEvent(int howMany) {
  while (0 < Wire.available()) {
    char c = Wire.read();      /* receive byte as a character */
    Serial.print(c);           /* print the character */
  }
  Serial.println();             /* to newline */
}

// function that executes whenever data is requested from master
void requestEvent() {
  Wire.write("Hello Master");  /*send string on request */
}

Kết quả

Đây là kết quả mình test thử giữa ESP8266/NodeMCU và Arduino Mega

SPI

Khái niệm

SPI  (Serial Peripheral Interface) là chuẩn giao tiếp dạng bus được đưa ra bởi Motorola Corp.

  • SPI dùng 4 chân kết nối, nên ta thường gọi nó là kết nối dạng 4 dây.
  • SPI là full duplex master-slave communication protocol. Có nghĩa là chỉ một master và một slave có thể kết nối nhau thông qua bus interface trong cùng một thời điểm.
  • SPI cho phép device có thể làm việc mở 2 mode cơ bản là  SPI Master Mode và SPI Slave Mode.
  • Master Device sẽ phản hồi thông tin thiết lập kết nối. Master Device tạo ra Serial Clock để đồng bộ data truyền nhận. Master Device còn có thể quản lý nhiều slave devices trên bus bằng việc lựa chọn từng cái.

ESP8266 có chân SPI (SD1, CMD, SD0, CLK) dùng cho Quad-SPI communication với flash memory trên ESP-12E, vì thếta sẽ không thể sử dụng những chân này được, ta sẽ dùng các chân thay thế là GPIO14-GPIO17, các bạn có thể xem hình dưới đây.

  • MISO (Master In Slave Out): Master nhận data và slave truyền data qua pin này.
  • MOSI (Master Out Slave In): Master truyền data và slave nhận data qua pin này.
  • SCLK (Serial Clock): Master tạo clock cho kết nối, slave dùng clock này. Chỉ duy nhất master có thể thiết lập được serial clock.
  • CS (Chip Select): Master có thể lựa chọn slave device thông qua pin này để bắt đầu kết nối với nó.

Sơ đồ kết nối

Chương trình

Chúng ta sẽ viết chương trình giao tiếp SPI cho ESP8266/NodeMCU và Uno/Mega, ở đây NodeMCU là master device và Uno/Mega là slave device.

NodeMCU sẽ gửi lời chào với chuỗi “Hello Slave” với ‘\n’ ở cuối chuỗi . Uno/Mega Slave device nhận chuỗi này và in nó ra serial.

Chương trình cho NodeMCU

#include<SPI.h>

char buff[]="Hello Slave\n";

void setup() {
 Serial.begin(9600); /* begin serial with 9600 baud */
 SPI.begin();  /* begin SPI */
}

void loop() {
 for(inti=0; i<sizeof buff; i++)  /* transfer buff data per second */
  SPI.transfer(buff[i]);
 delay(1000);  
}

Chương trình cho Mega/Uno

#include <SPI.h>

char buff [100];
volatile byte index;
volatile bool receivedone;  /* use reception complete flag */

void setup (void)
{
  Serial.begin (9600);
  SPCR |= bit(SPE);         /* Enable SPI */
  pinMode(MISO, OUTPUT);    /* Make MISO pin as OUTPUT */
  index = 0;
  receivedone = false;
  SPI.attachInterrupt();    /* Attach SPI interrupt */
}

void loop (void)
{
  if (receivedone)          /* Check and print received buffer if any */
  {
    buff[index] = 0;
    Serial.println(buff);
    index = 0;
    receivedone = false;
  }
}

// SPI interrupt routine
ISR (SPI_STC_vect)
{
  uint8_t oldsrg = SREG;
  cli();
  char c = SPDR;
  if (index <sizeof buff)
  {
    buff [index++] = c;
    if (c == '\n'){     /* Check for newline character as end of msg */
     receivedone = true;
    }
  }
  SREG = oldsrg;
}

Kết quả

Kiểm tra trên terminal của Uno sẽ có thông tin "Hello Slave"

Tạm kết

Vậy là mình đã thực hiện giao tiếp truyền nhận data từ ESP8266/NodeMCU sang Arduino Uno/Mega với các chuẩn giao tiếp UART,I2C, SPI rồi, giờ thì các bạn có thể thoải mái sáng tạo và thử nghiệm với dự án của mình nhé.

Tham khảo

[1][2][3]