آموزش برقراری ارتباط دو برد ESP32 با پروتکل I2C
محتویات
در این مقاله نحوه ارسال و دریافت اطلاعات بین دو برد ESP32 را با استفاده از پروتکل ارتباطی I2C یاد میگیرید. یک برد ESP32 به عنوان مستر I2C و برد دیگر به عنوان یک اسلیو I2C تنظیم می شود. بردهای ESP32 با استفاده از Arduino IDE برنامه ریزی خواهند شد.
معرفی I2C
I²C به معنای مدار یکپارچه (Interintegrated Circuit) است و یک پروتکل ارتباطی همزمان، چند مستر و چند اسلیو است. برای جزئیات کامل مقاله I2C چیست را بخوانید.
- چندین slave به یک Master: برای مثال، ESP32 شما از حسگر BME280 با استفاده از I2C می خواند و قرائت سنسور را در یک صفحه نمایش OLED I2C می نویسد.
- چندین Master که یک Slave را کنترل می کنند: برای مثال، دو برد ESP32 داده ها را روی یک صفحه نمایش OLED I2C می نویسند.
ESP32 از ارتباط I2C از طریق دو باس I2C خود پشتیبانی می کند که بسته به پیکربندی کاربر می تواند به عنوان مستر یا اسلیو I2C عمل کند. بر اساس دیتاشیت ESP32، رابط های I2C این برد از موارد زیر پشتیبانی می کنند:
- حالت استاندارد (100 کیلوبیت بر ثانیه)
- حالت سریع (400 کیلوبیت بر ثانیه)
- حالت آدرس دهی 7 بیتی/10 بیتی
حالت آدرس دهی دوگانه
برقراری ارتباط I2C بین دو ESP32
برای برقراری ارتباط بین دو برد ما از کتابخانه پیشفرض Wire و پایه های پیشفرض برد ESP32 استفاده میکنیم. کار را با اتصال دو برد به یکدیگر شروع میکنیم. فراموش نکنید که پایه های GND را به هم وصل کنید.
ESP32 Master | ESP32 Slave |
SDA (GPIO 21)* | SDA(GPIO 21)* |
SCL (GPIO 22)* | SCL (GPIO 22)* |
GND | GND |
* من این را با برد ESP32 DOIT V1 آزمایش می کنم. پین های پیش فرض I2C موارد GPIO 21 (SDA) و GPIO 22 (SCL) هستند. اگر از ESP32-C3، ESP32-S3 یا مدل های دیگر استفاده می کنید، پین های پیش فرض I2C ممکن است متفاوت باشند. لطفاً پین اوت را برای بردی که استفاده میکنید بررسی کنید.
اصول ارتباط I2C بین 2 برد ESP32
در اینجا نحوه ارتباط I2C بین دو برد ESP32 کار می کند:
ESP32 Master | ESP32 Slave |
آدرس I2C خود را تنظیم می کند توابع پاسخ به تماس را تنظیم می کند: – خواندن پیام های دریافتی؛ – رسیدگی به درخواست ها | |
گذرگاه I2C را در آدرس اسلیو I2C راه اندازی می کند ارتباط I2C را در گذرگاه I2C شروع می کند | |
ارسال پیام از طریق I2C (انتقال را شروع می کند) | |
پیام دریافتی را می خواند | |
اطلاعات را از اسلیو درخواست می کند | |
داده ها را برای مستر ارسال می کند |
کد آردوینو برای ESP32 اسلیو I2C
برای آزمایش تنظیم ESP32 به عنوان یک اسلیو I2C از کد زیر استفاده میکنیم.
#include "Wire.h" #define I2C_DEV_ADDR 0x55 uint32_t i = 0; void onRequest() { Wire.print(i++); Wire.print(" Packets."); Serial.println("onRequest"); Serial.println(); } void onReceive(int len) { Serial.printf("onReceive[%d]: ", len); while (Wire.available()) { Serial.write(Wire.read()); } Serial.println(); } void setup() { Serial.begin(115200); Serial.setDebugOutput(true); Wire.onReceive(onReceive); Wire.onRequest(onRequest); Wire.begin((uint8_t)I2C_DEV_ADDR); /*#if CONFIG_IDF_TARGET_ESP32 char message[64]; snprintf(message, 64, "%lu Packets.", i++); Wire.slaveWrite((uint8_t *)message, strlen(message)); Serial.print('Printing config %lu', i); #endif*/ } void loop() { }
بیایید نگاهی گذرا به نحوه عملکرد کد اسلیو بیندازیم. ابتدا باید کتابخانه Wire.h را وارد کنید.
#include "Wire.h"
آدرس I2C را که می خواهید به دستگاه I2C خود بدهید، تعریف کنید. در این مورد، 0x55 است، اما تا زمانی که از همان آدرس در دستگاه اصلی استفاده می کنید، می توانید هر آدرس دیگری را تعریف کنید.
#define I2C_DEV_ADDR 0x55
در setup()، باید دو تابع callback اختصاص دهید: یکی برای زمانی که برد دادهها را از Master دریافت میکند و دیگری برای زمانی که برد درخواستی از Master دریافت میکند (ارسال داده ها به Master).
Wire.onReceive(onReceive); Wire.onRequest(onRequest);
تابع onReceive() داده های ارسال شده از Master را خوانده و روی مانیتور سریال چاپ می کند.
void onReceive(int len) { Serial.printf("onReceive[%d]: ", len); while (Wire.available()) { Serial.write(Wire.read()); } Serial.println(); }
با استفاده از Wire.available() بررسی میکنید که آیا دادههایی برای خواندن موجود است یا خیر.
while (Wire.available()) {
تابع onRequest() در صورت درخواست داده ها را به Master ارسال می کند.
void onRequest() { Wire.print(i++); Wire.print(" Packets."); Serial.println("onRequest"); Serial.println(); }
برای ارسال داده ها به Master، از Wire.print() استفاده می کنیم.
Wire.print(i++); Wire.print(" Packets.");
در setup()، باید ارتباط I2C را روی آدرس I2C که قبلاً تعریف شده بود، راه اندازی کنید. از متد begin() به صورت زیر استفاده کنید. با این کار I2C روی پین های I2C پیش فرض ESP32 مقداردهی اولیه می شود.
Wire.begin((uint8_t)I2C_DEV_ADDR);
همچنین می توانید پین های I2C و فرکانس را به این متد منتقل کنید:
bool Wire.begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency);
کد آردوینو برای مستر I2C
برای تست تنظیم ESP32 به عنوان یک I2C مستر، از کد زیر استفاده میکنیم.
#include "Wire.h" #define I2C_DEV_ADDR 0x55 uint32_t i = 0; void setup() { Serial.begin(115200); Serial.setDebugOutput(true); Wire.begin(); } void loop() { delay(5000); // Write message to the slave Wire.beginTransmission(I2C_DEV_ADDR); Wire.printf("Hello World! %lu", i++); uint8_t error = Wire.endTransmission(true); Serial.printf("endTransmission: %u\n", error); // Read 16 bytes from the slave uint8_t bytesReceived = Wire.requestFrom(I2C_DEV_ADDR, 16); Serial.printf("requestFrom: %u\n", bytesReceived); if ((bool)bytesReceived) { //If received more than zero bytes uint8_t temp[bytesReceived]; Wire.readBytes(temp, bytesReceived); log_print_buf(temp, bytesReceived); } }
بیایید نگاهی گذرا به نحوه کارکرد کد مستر ESP32 I2C بیندازیم.
ابتدا باید کتابخانه Wire.h را وارد کنید.
#include "Wire.h"
آدرس I2C دستگاه اسلیو را تنظیم کنید (در کد قبلی تنظیم شده است):
#define I2C_DEV_ADDR 0x55
در setup()، ارتباط I2C را با استفاده از Wire.begin() مقداردهی اولیه کنید. با این کار I2C روی پین های پیش فرض I2C راه اندازی اولیه می شود.
Wire.begin();
همچنین می توانید پین های I2C و فرکانس را به این متد منتقل کنید:
bool Wire.begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency);
در loop()، پیامی به Slave میفرستیم تا اطلاع دهیم که انتقال I2C را شروع خواهیم کرد:
Wire.beginTransmission(I2C_DEV_ADDR); Wire.printf("Hello World! %lu", i++); uint8_t error = Wire.endTransmission(true); Serial.printf("endTransmission: %u\n", error);
ابتدا باید متد ()fillTransmission را فراخوانی کنید و آدرس اسلیو را قبل از نوشتن پیام ارسال کنید.
Wire.beginTransmission(I2C_DEV_ADDR);
سپس، با استفاده از متد printf() یک پیام به بافر می نویسید.
Serial.printf("endTransmission: %u\n", error);
در نهایت برای ارسال پیام بافر شده باید از متد endTransmission() استفاده کنید.
uint8_t error = Wire.endTransmission(true);
سپس، با استفاده از Wire.requestFrom() دادهها را از Slave درخواست میکنیم. آدرس Slave و تعداد بایت های درخواستی را به عنوان آرگومان ارسال کنید.
uint8_t bytesReceived = Wire.requestFrom(I2C_DEV_ADDR, 16);
سپس تمام بایت ها را به هم متصل کرده و داده های دریافتی را چاپ کنید.
Serial.printf("requestFrom: %u\n", bytesReceived); if ((bool)bytesReceived) { uint8_t temp[bytesReceived]; Wire.readBytes(temp, bytesReceived); log_print_buf(temp, bytesReceived); }
بررسی عملکرد ارتباط I2C بین دو میکروکنترلر ESP32
کد های Master و Slave را روی بردهای ESP32 خود آپلود کنید. دو نمونه از Arduino IDE را باز کنید تا Serial Monitor را برای هر دو برد به صورت همزمان ببینید. باید مشاهده کنید که Master یک ارتباط با Slave را راه اندازی می کند و بسته های داده را دریافت می کند.
این همان چیزی است که باید در Slave دریافت کنید (پیام Hello World! به دنبال آن یک شمارنده و تابع onRequest فعال می شود):
در Master، بسته های ارسال شده از Slave را دریافت خواهید کرد.
در این آموزش، نحوه تنظیم ESP32 را به عنوان یک اسلیو I2C و به عنوان یک مستر I2C و نحوه تبادل داده بین دو برد EPS32 با استفاده از پروتکل ارتباطی I2C به شما نشان دادیم.